Skip to content

029_gui_canvas 排查笔记

问题 1:Canvas 测试后内核 hang(test_fifo_ordering)

现象

GUI ON 时,Canvas 测试全部通过后,test_fifo_ordering 在创建 fifo_owner 后 hang。GUI OFF 时基线 356 测试全过。

根因

Heap expand 无上限检查。KMEM_HEAP_SIZE = 1MB(仅初始大小),Heap 可无限 expand。Canvas 每次 init() 分配 ~3MB back_buf(1024x768x4),Heap 扩展到 ~3MB 后越界进入 MMIO/Stack 虚拟地址区域,导致 VMM 页表映射冲突,后续 TaskBuilder 分配栈时 g_vmm.map() 行为异常。

修复

  • heap.hpp:新增 max_size_ 字段
  • heap.cppinit() 中设置 max_size_ = KMEM_HEAP_SIZEexpand() 中检查 size_ + expand_size > max_size_ 则返回 false
  • memory_layout.hppKMEM_HEAP_SIZE 从 1MB 提到 128MB(GUI OS 合理值,物理页按需分配不浪费)

问题 2:test_create_user_space hang

现象

Heap 修复后,GUI ON 时 test_create_user_space hang。AddressSpace 构造中 phys_to_virt(pml4_phys_) = phys + 0xFFFFFFFF80000000,当 PMM 分配的物理页超过 loader 映射范围时 page fault。

根因

Loader 的 identity_map_up_to() 只映射到 ELF 段末尾(~20MB)。PMM 管理 9GB 物理内存,alloc_page() 可返回任意地址。当返回 >20MB 的物理页时,phys_to_virt() 返回的虚拟地址未被映射,访问即 page fault。

Canvas 测试消耗了大量低地址物理页(Heap expand),使后续分配更容易落到高地址区域。

修复

  • big_kernel_loader.cpp:phase2 中扫描 BootInfo 的 E820 memory map,找最高可用 RAM 地址,映射全部物理内存(Linux-style full direct map)
  • 用 2MB 大页映射 0-1GB,1GB 大页映射 >=1GB,开销极小(8GB 仅需 ~5 页页表)

附带修复

  • test_vmm.cpptest_demand_page 原来依赖地址未映射来触发 page fault,全量 direct map 后该假设不成立。改为验证高地址可通过 direct map 正常读写。

虚拟内存布局(修复后)

0xFFFF8000_00000000  KMEM_HEAP_BASE     (128 MB reserved)
0xFFFF8000_08000000  KMEM_MMIO_BASE     (256 KB)
0xFFFF8000_08040000  KMEM_STACK_BASE    (1 MB, 向上增长)
0xFFFF8000_08140000  KMEM_DMA_BASE      (1 MB)
0xFFFF8000_08240000  KMEM_EXT2_DMA_BASE (1 MB)
...
0xFFFF_FFFF_80000000  KERNEL_VMA (direct physical map, 全量)

教训

  1. Heap expand 必须有上限:不能假设"虚拟地址空间无限就随便扩展",必须检查预留区域边界
  2. phys_to_virt 依赖 direct map 覆盖:loader 只映射 ELF 段是不够的,PMM 可能返回任意物理地址,必须全量映射或实现 kmap
  3. 测试要覆盖资源消耗场景:大块分配(Canvas 3MB)在单元测试中不可见,只有内核集成测试才会暴露内存布局冲突

035_multi_terminal-40-g5d72b8b · 5d72b8b · 2026-06-26