PageFault
出现
一般应用程序的对虚拟内存的“需 求”与物理内存空间的“供给”没有直接的对应关系,ucore是通过page fault异常处理来间接完成 这二者之间的衔接。
当我们引入了虚拟内存,就意味着虚拟内存的空间可以远远大于物理内存,意味着程序可以访问"不对应物理内存页帧的虚拟内存地址",这时CPU应当抛出Page Fault这个异常。
处理
pgfault_handler
pgfault_handler回想一下,我们处理异常的时候,是在kern/trap/trap.c的exception_handler()函数里进行的。按照scause寄存器对异常的分类里,有CAUSE_LOAD_PAGE_FAULT 和CAUSE_STORE_PAGE_FAULT两个case。之前我们并没有真正对异常进行处理,只是简单输出一下就返回了。现在我们要真正进行Page Fault的处理。
// kern/trap/trap.c
static inline void print_pgfault(struct trapframe *tf) {
cprintf("page falut at 0x%08x: %c/%c\n", tf->badvaddr,
trap_in_kernel(tf) ? 'K' : 'U',
tf->cause == CAUSE_STORE_PAGE_FAULT ? 'W' : 'R');
}
static int pgfault_handler(struct trapframe *tf) {
extern struct mm_struct *check_mm_struct;
print_pgfault(tf);
if (check_mm_struct != NULL) {
return do_pgfault(check_mm_struct, tf->cause, tf->badvaddr);
}
panic("unhandled page fault.\n");
}
void exception_handler(struct trapframe *tf) {
int ret;
switch (tf->cause) {
/* .... other cases */
case CAUSE_FETCH_PAGE_FAULT:// 取指令时发生的Page Fault先不处理
cprintf("Instruction page fault\n");
break;
case CAUSE_LOAD_PAGE_FAULT:
cprintf("Load page fault\n");
if ((ret = pgfault_handler(tf)) != 0) {
print_trapframe(tf);
panic("handle pgfault failed. %e\n", ret);
}
break;
case CAUSE_STORE_PAGE_FAULT:
cprintf("Store/AMO page fault\n");
if ((ret = pgfault_handler(tf)) != 0) { //do_pgfault()页面置换成功时返回0
print_trapframe(tf);
panic("handle pgfault failed. %e\n", ret);
}
break;
default:
print_trapframe(tf);
break;
}
}这里的异常处理程序,把Page Fault分发给kern/mm/vmm.c的do_pgfault()函数,尝试进行页面置换。接下来我们处理多级页表。之前的初始页表占据一个页的物理内存,只有一个页表项是有用的,映射了一个大大页(Giga Page)。
地址转换
之前我们物理页帧管理有个功能没有实现,那就是动态的内存分配。管理虚拟内存的数据结构(页表)需要有空间进行存储,而我们又没有给它预先分配内存(也无法预先分配,因为事先不确定我们的页表需要分配多少内存),就需要有malloc/free的接口来分配释放内存。我们在这里顺便看看pmm.h里对物理页面和虚拟地址,物理地址进行转换的一些函数。
最后更新于
这有帮助吗?