问题描述
完成清华大学操作系统实验课 ucore(x86) lab3
时,发现无法触发 page fault
异常,具体来说时 check_pgfault()
函数会在执行如下代码时报错
static void
check_pgfault(void) {
// ......
uintptr_t addr = 0x100;
assert(find_vma(mm, addr) == vma);
int i, sum = 0;
for (i = 0; i < 100; i ++) {
*(char *)(addr + i) = i;
sum += i;
}
for (i = 0; i < 100; i ++) {
sum -= *(char *)(addr + i);
}
assert(sum == 0);
page_remove(pgdir, ROUNDDOWN(addr, PGSIZE));
free_page(pde2page(pgdir[0]));
// .......
}
这段代码的意思是,通过往 0x100
附近写入数据时,触发 page fault
异常,然后异常处理例程为 pgdir
第一个目录项分配一个页表,于是执行完后,pgdir[0]
应该指向了第一个页表的物理地址才对。
在代码中对 trap_dispatch()
函数添加断点,当执行到 *(char *)(addr + i) = i
时,代码并没有触发异常,并且此时 pgdir[0] = 0
,可见结果不符合预期。
联想到在 lab2
的 pmm_init()
函数,最后页目录取消了对 0~4M
地址空间的映射,而上面的代码中访问的地址正好也位于 0~4M
,所以可能和该原因有关。
void
pmm_init(void) {
// ....
//disable the map of virtual_addr 0~4M
boot_pgdir[0] = 0;
check_boot_pgdir();
print_pgdir();
}
从 pmm_init()
看到,执行 boot_pgdir[0] = 0
后没有执行 tlb_invalidate
函数,此时如果页表被缓存了,那么再次访问的时候就不会触发 page fault
了。
解决方法
在 pmm_init()
函数的结尾添加一句 tlb_invalidate(boot_dir, 0)
。
void
pmm_init(void) {
// ....
//disable the map of virtual_addr 0~4M
boot_pgdir[0] = 0;
check_boot_pgdir();
print_pgdir();
++ tlb_invalidate(boot_dir, 0);
再次编译运行,可以看到输出信息里包含了触发 page fault
的日志了。
--------------------- END ---------------------
check_vma_struct() succeeded!
page fault at 0x00000100: K/W [no page found].
check_pgfault() succeeded!