Lab: page tables
Speed up system calls(easy)
实验思路
- 首先是看 kernel/memlayout.h ,能看到里面有一个
USYSCALL的宏,还有一个struct usyscall的结构体,看题能知道,这题就是希望使用这个usyscall让用户态的程序获取 pid 的时候不切换内核态 - 根据提示,找到 kernel/proc.c 的
proc_pagetable()函数,在这里可以实现映射,根据上一条思路,和这里的这个函数一起观看会发现,这个函数还实现了了另外两个宏的映射,因此,模仿着它的映射,就可以了。但是会发现proc结构体里没有宏对应的变量,因此需要手动添加一个 - 提示里还说要注意选择权限的比特位,由于不知道权限的比特位,需要看一下书,看完书就知道比特位在 kernel/riskv.h 中也有宏的定义,直接使用就好了
- 根据提示,在
allocproc()函数中,需要初始化usyscall,同时也能看到这个函数里初始化另外一个宏,在它下面模仿着初始化就可以了 - 根据提示,和上一条思路一样修改方法,修改
freeproc() - 这时候运行会出现
panic: freewalk: leaf的错误,看到 free ,猜到也许是freeproc()的问题,仔细看修改过的代码,没发现什么问题,由于改动和页表相关,去看一下proc_freepagetable(),发现这里没有 ummap USYSCALL ,添加一条 ummap USYSCALL 就可以了
踩到的坑
- 在
allocproc()函数中,我把初始化 usyscall 写到了初始化页表后面,导致初始化页表出现了缺页异常,找了了十分久才发现
Print a page table(easy)
实验思路
- 定义一个
vmprint()函数在 kernel/vm.c 中,由于是打印页表,因此参数就是一个页表 - 根据示例,直接使用
%p打印第一行地址,也就是页表的第一条地址 - 页表的转换需要使用位运算,这个在 kernel/riscv.h 中有宏定义好了,页表的其他信息里面也有宏定义好了,直接调用即可
- 参考
freewalk()函数,是释放页表,释放页表需要遍历,因此可以直接将整个函数复制过来修改,但是由于需要递归操作,所以新建一个_print_pgtbl()函数,将freewalk()复制进去,新函数的参数是页表和层级,层级用于判断递归的层数 - 页表中有一个位是
PTE_V,在 kernel/riscv.h 可以得知,这个位是判断页表是否有效的。对于无效的页表,直接跳过就好,对于有效的页表,直接按格式打印层级和地址 - 打印完之后需要判断该页表是不是叶子的页表,判断的方法通过课程或者 xv6 book 可以知道,当
PTE_R PTE_W PTE_X都等于 0 的时候,页表有子页表,否则是叶子页表,如果不是叶子页表,直接递归同时层级加一就行了 - 总结一些递归思路,首先遍历
pagetable,取出每一个pte,如果pte是有效的继续,无效则结束,然后遍历层级,打印..,接着获取该pte的pa,按照格式打印出来,最后判断该pte是不是叶子pte,如果不是就递归
Detecting which pages have been accessed (hard)
实验思路
- 首先要找到
kernel/sysproc.c里面的sys_pgaccess(),然后使用argaddr()和argint()接收参数,可以参考一下其他地方的使用,前面说了会有三个参数,如果感觉不明确,可以去user/pgtbltest.c里看一下。 - 根据提示,使用
copyout()传出参数,可以使用printf(),测试一些。 - 根据提示,做边界测试。
- 使用
walk()函数,这个函数的作用就是根据虚拟地址返回物理地址,如果没有找到,就会根据alloc判断是否新建 pagetable ,这里不需要。 - 在
kernel/vm.c下新建一个函数,否则调用不了,kernel/sysproc.c中没有walk()函数的定义,新函数使用walk()获取 pte ,然后定义一个PTE_A的宏,用于得到 access 的标志位。当获取到标志位为 1 时,设置为 0 并返回 1 。 - 通过位运算得到结果,通过
copyout()传递出去。