lab3 页表
加速getpid
核心就是为每一个进程的单独内存空间添加一个USYSCALL页,这样用户程序可无需陷入内核中,而直接从这个页面获取数据(在本实验中为pid),进而省去系统开销,加速系统调用。
根据实验文档,USYSCALL相关的定义在memlayout.h文件中:
// User memory layout.
// Address zero first:
// text
// original data and bss
// fixed-size stack
// expandable heap
// ...
// USYSCALL (shared with kernel)
// TRAPFRAME (p->trapframe, used by the trampoline)
// TRAMPOLINE (the same page as in the kernel)
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
#ifdef LAB_PGTBL
#define USYSCALL (TRAPFRAME - PGSIZE)
struct usyscall {
int pid; // Process ID
};
#endif

memlayout.h文件中主要是用户空间内存布局的定义,上图为xv6手册中给出的进程的用户地址空间,可以看到与代码的注释是对应的,虚拟地址空间从0到MAXVA,依次是
text:程序代码段
data(bss):程序的数据
guard page:检测用户栈是否溢出了分配的栈内存
stack:固定大小的用户栈
heap:可拓展的堆区
trapframe:陷入内核时保存用户上下文的区域
trampoline:用户态跳转到内核态的特殊“跳板”页,与内核共享,用于进入内核。
USYSCALL定义应该是位于trapframe和heap之间,单独开辟一个新页来存储数据。首先要在proc.h的proc结构体中添加usyscall变量,然后修改proc.c文件中关于进程和映射的函数。在allocproc分配物理页时添加分配usyscall空间的代码,并将pid对应存储;在proc_pagetable中建立虚拟地址到物理地址的映射,设置权限用户可读;在freeproc中释放进程资源时释放掉新加的usyscall页的空间,并且由于在释放时调用了proc_freepagetable释放页表中对应的页表项,还需要在这个函数中增加解除usyscall映射的代码。
PTE标志位
- PTE_P:PTE是否陈列在页表中
- PTE_W:能否对页执行写操作
- PTE_U:控制用户程序能否使用该页
- PTE_R:用户能否读取
- PTE_V:PTE是否存在或有效,若不存在,则尝试引用时会报缺页错误。
函数解释
-
mappages
在页表中建立虚拟地址到物理地址的映射,并设置相应的访问权限。
-
uvmfree
释放一个进程所使用的用户空间内存,并销毁与之相关的页表(包括页目录和页表本身使用的物理内存)。
-
kfree
当某个物理页不再被使用时,释放这个物理页,并将其返回给内核的空闲页管理系统。
-
uvmunmap
取消一段虚拟地址范围内的页表映射,即把虚拟地址和物理地址的绑定关系解除
-
freewalk
递归释放页表结构所有的中间页。利用递归的思想,递归遍历页表项,取出每个页表项,判断它是否有效、是否是中间页表(是指向下一级页表而非指向物理页表的),利用PTE2PA宏从PTE中提取物理地址,然后将这个子页表递归地传递给freewalk释放,把当前页表项清零,表示它不再有效。若不是中间页而是叶子页表,则不应该释放,打印报错的信息。最后递归地处理完所有的子页表后,释放当前这个页表本身所占的物理页。
编写vmprint函数
首先查看利用递归来释放页表结构所有的中间页。首先遍历页表项,取出每个页表项,判断它是否有效、是否是中间页表(即是指向下一级页表而非指向物理内存的),是的话,利用PTE2PA宏从PTE中提取物理地址,然后将这个子页表递归地传给freewalk释放,把当前页表项清零,表示它不再有效。若不是中间页表,而是叶子页表,则不应该释放,打印报错信息。最后递归地处理完所有的子页表后,释放当前这个页表本身所占的物理页。
// Recursively free page-table pages. // All leaf mappings must already have been removed. void freewalk(pagetable_t pagetable) { // there are 2^9 = 512 PTEs in a page table. for(int i = 0; i < 512; i++){ pte_t pte = pagetable[i]; if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ // this PTE points to a lower-level page table. uint64 child = PTE2PA(pte); freewalk((pagetable_t)child); pagetable[i] = 0; } else if(pte & PTE_V){ panic("freewalk: leaf"); } } kfree((void*)pagetable); }仿照freewalk的递归思路,另外设置一个printdeep的变量来指示打印当前页的深度。初始深度为0打印当前页表,然后遍历当前页表的所有页表项,如果是有效的叶子页表则打印出当前的索引、页表项的值和物理地址,并根据深度打印缩放,如果是中间页表,则增加深度,调用vmprint递归打印子页表,回溯完成后再将深度减一。
static int printdeep = 0;
void
vmprint(pagetable_t pagetable)
{
if (printdeep == 0)
printf("page table %p\n", (uint64)pagetable);
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
if (pte & PTE_V) {
for (int j = 0; j <= printdeep; j++) {
printf("..");
}
printf("%d: pte %p pa %p\n", i, (uint64)pte, (uint64)PTE2PA(pte));
}
// pintes to lower-level page table
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
printdeep++;
uint64 child_pa = PTE2PA(pte);
vmprint((pagetable_t)child_pa);
printdeep--;
}
}
}
最后在defs.h中添加定义:
void vmprint(pagetable_t);
1327

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



