虚拟内存 - 常见错误
错误 1:启用分页后立即Triple Fault
症状
设置CR0的PG位后,系统立即重启。
原因
当前正在执行的代码没有被映射。启用分页后CPU立即使用虚拟地址,如果这些地址未映射到正确的物理内存,就会缺页→双重错误→Triple Fault。
解决方案
在启用分页之前,必须建立恒等映射(identity mapping),至少覆盖:
- 内核代码所在的内存
- 栈
- VGA显存(如果需要输出)
- 页目录和页表本身
错误 2:页目录/页表未对齐
症状
加载CR3后系统崩溃。
原因
页目录和页表必须4KB对齐(地址低12位为0)。
解决方案
// 使用页对齐的kmalloc
page_directory_t *dir = kmalloc_aligned(sizeof(page_directory_t));
page_table_t *table = kmalloc_aligned(sizeof(page_table_t));错误 3:忘记设置Present位
症状
访问映射好的地址仍然产生缺页异常。
原因
页表条目的Present位未设置。
解决方案
// 确保flags中包含PAGE_PRESENT
table->entries[pt_idx] = (phys & 0xFFFFF000) | PAGE_PRESENT | PAGE_WRITABLE;
// ^^^^^^^^^^^^^ 关键!错误 4:TLB缓存导致映射不生效
症状
修改了页表但访问仍使用旧映射。
原因
CPU缓存了虚拟→物理的映射在TLB中。
解决方案
修改页表后刷新相应的TLB条目:
void vmm_flush_tlb(uint32_t virt) {
__asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory");
}或重新加载CR3(刷新整个TLB):
__asm__ volatile("mov %0, %%cr3" : : "r"(dir->physical_addr));错误 5:页目录/页表条目混淆
症状
映射看似正确但访问时出错。
原因
页目录条目应该指向页表的物理地址,而不是虚拟地址。
解决方案
在恒等映射环境下,虚拟=物理,所以问题不明显。但在高半内核中:
// 页目录条目必须使用物理地址
dir->entries[pd_idx] = phys_addr_of_table | flags;
// 内核访问页表使用虚拟地址
dir->tables[pd_idx] = virt_addr_of_table;调试技巧
# QEMU显示页表内容
(qemu) info tlb # 查看TLB
(qemu) info mem # 查看内存映射
(qemu) xp /4x <cr3> # 查看页目录内容
# GDB查看CR3
(gdb) p/x $cr3
(gdb) p/x $cr2 # 缺页异常的地址