本Lab简单优化了系统的页表功能,使得程序在内核态时可以直接解析用户态的指针。
笔者用时约8h
Print a page table
第一部分是为系统添加一个打印给定页表的函数vmprint,该函数接收一个参数pagetable(根页表的物理地址),递归遍历整张页表,打印有效的表项。
参考freewalk函数(定义在kernel/vm.c:331),每次遍历512个表项,若表项有效,则打印相关信息(第几级、第几项、pte内容和pte内容对应的物理地址),且若为一二级页表则继续递归,直到第三级页表返回。参考代码如下:
void
vmprint_helper(pagetable_t pgtbl, int level)
{
// there are 2^9 = 512 PTEs in a page table.
for(int i = 0; i < 512; i++){
pte_t pte = pgtbl[i];
if(pte & PTE_V){
for (int j = 0; j < level; j ++ ) {
if (j) printf(" ");
printf("..");
}
printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte));
if ((pte & (PTE_R|PTE_W|PTE_X)) == 0) {
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
vmprint_helper((pagetable_t)child, level + 1);
}
}
}
}
void
vmprint(pagetable_t pgtbl)
{
printf("page table %p\n", pgtbl);
vmprint_helper(pgtbl, 1);
}
A kernel page table per process
如标题,第二部分的内容是为每一个进程添加一个单独的内核页表副本,为下一节直接解引用用户态指针做铺垫。
首先需要在进程的结构体(定义在kernel/proc.h中)struct proc中添加一个字段维护内核页表副本,如下图所示

然后,由于我们需要在分配进程时需要为每一个进程初始化一个内核页表的副本,于是需要参考kvminit函数(定义在kernel/vm.c:66),编写一个初始化进程中内核页表副本的函数proc_kvminit,代码如下所示。该函数内容与kvminit函数基本一致。其中的uvmmap与kvmmap函数(定义在kernel/vm.c:171)类似,映射给定的虚拟地址和物理地址范围,唯一不同点是前者修改的是传入的指定页表而不仅仅是全局的内核页表。
void
uvmmap(pagetable_t pagetable, uint64 va, uint64 pa, uint64 sz, int perm)
{
if(mappages(pagetable, va, sz, pa, perm) != 0)
panic("uvmmap");
}

本文介绍了如何优化操作系统页表功能,包括添加打印页表的函数vmprint,每个进程拥有独立的内核页表副本,以及简化copyin/copyinstr操作。通过proc_kvminit初始化进程内核页表,并在scheduler中切换页表,实现进程间内核页表的独立。同时,实现了uvm2ukvm函数,将用户空间映射复制到内核页表,方便内核直接访问用户空间数据。
最低0.47元/天 解锁文章
2297

被折叠的 条评论
为什么被折叠?



