你说的 easy 不是 easy,我说的 hard 是什么 hard😭,实验难度 easy
, easy
, hard
,结果第一题差点把我送走了……MIT,你坏事做尽 😭
实验准备
把第三章看懂(条件很简单,也很 tm 难,第三章应该是我目前为止没有把英文原本读完的一章了,实在是看不下去啊 😭),可以读中文版,也可以阅读*《现代操作系统:原理与实现》*(处理器架构有所不同但无伤大雅)。
其实把网站上橙色部分都做完就行。
不如给网站:Lab: page tables (mit.edu)
请务必完成前置要求,否则你根本不知道应该修改什么(或者是不知道为什么要修改)
还有要记住的就是:fllow the hints
Speed up your system calls
在进程被创建的同时创建一个只读页面,以便内核与用户程序的交互,这样就可以不需要每次系统调用的时候都去请求读写了,典型的一种空间换时间的提高速度的方法。这里只要求优化getpid()
这个系统调用。
官方能不能把函数在那个文件都说一下啊!一个一个去找真的很累啊焯
首先,我们可以《很轻易》的在 user/ulib.c
中找到 ugetpid()
的定义:
会发现这是灰的,但是没事!注意到第一行有一个 LIB_PGTBL
,也就是当你在做 PGTBL
这个实验的时候,这个函数是可以被使用的,否则就不行。
分析这个函数,可以发现用了一个 struct usyscall
,还好 MIT 告诉我们在哪里可以找到这个: kernel/memlayout.h
,我们可以在里面找到一起出现的一个常量 USYSCALL
。
我们可以发现,这个 u
指向的位置就是 USYSCALL
的地址,也就是说 USYSCALL
处应当存储一个 struct usyscall
才可以,根据对 ugetpid()
的描述,我们知道 USYSCALL
就存在需要我们创建的只读页表之中。
我们查看 USYSCALL
的定义:
可以发现 USYSCALL
在跳板下两个 PGSIZE
的位置(可以看教材中的图来确定位置,虽然这对实验没什么作用)
接下来跟着提示来:
- 可以在
kernel/proc.c
中的proc_pagetable()
中执行映射 - 页面必须是只读的(且用户可操作)
- 在
allocproc()
初始化进程时,开辟这个页面(初始化) - 需要在
freeproc()
中释放这个页面(当进程被kill
时)
那不妨从开辟页面开始:
这里的代码是仿照开辟 trapframe
页面的代码写的,当然,我们在这之前,还需要在 kernel/proc.h
中为 struct proc
添加一个成员变量 stryct usyscall* usyscall
,以储存这个页面的地址。
在开辟完页面后,我们还需要将 pid
存进这个页面中,也就是 struct usyscall
的 pid
中。
然后,我们需要将新开辟的页面映射到物理内存上(开辟页面一律指在虚拟内存中开辟),在proc_pagetable()
中添加:
这里需要注意两点:
- 由于是只读的,我们需要添加
PTE_R
,但注意到注释中有解释,若想在用户空间中调用,需要添加PTE_U
- 在映射失败时,需要取消映射,但我们需要把前两个都取消了,为什么能想到,不妨看看上面两个
if
后面都跟着什么(顺序需要从后往前取消,因为后一个是依赖于前一个的)
最后就是删除这个页面:
同样是仿照如何删除 trapframe
页面。但这里我们还需要在 proc_freepagetable
中释放页面映射的物理地址:
取消虚拟内存到物理内存的映射即可。
回答一下网页上的问题:还有哪些系统调用是可以通过这种方式加速的?
这个问题很简单,这种方法本质上只是内核为用户共享了一个只读页面,因此那些需要读取内核信息的系统调用都可以使用这种方式来加速。
Print a page table
在 kernel/vm.c
中实现一个遍历进程的页表的函数 vmprint(pagetable_t pagetable)
,当进程的pid == 1
时执行。
实现的方式可以参照同文件下的函数 freewalk()
。
显然,我们还需要在 kernel/defs.h
中注册这个函数。
最后,在 kernel/exec.c
中 return argc
之前调用即可。(这个难度确实是 easy,没骗人)
Explain the output of
vmprint
in terms of Fig 3-4 from the text. What does page 0 contain? What is in page 2? When running in user mode, could the process read/write the memory mapped by page 1? What does the third to last page contain?或许可以画个图来描述?
方框中数字表示索引,长方形的解释可以参见教材中的插图:
参照教材中对PTE
的解释,我们来分析后面的问题:
0x0000000021fda00f
分析后十位:0000001111
,可以发现是可读可写,且可被用户访问的。
Detecting which pages have been accessed
这个题一点也不 hard
。。
从一个用户页表地址开始,搜索所有被访问过的页表并返回一个bitmap
来显示这些页是否被访问过(一个比特串,从右往左数,第 位若为 则代表第 个页面被访问过)。
需要在 kernel/sysproc.c
中实现这个函数。
当然,我们可以继续跟着提示做(这个题因为有提示所以一点都不难了)
主要注意四点:
- 我们需要在
kernel/defs.h
中注册walk()
函数才可以使用(不知道为什么这个函数是存在的但是没被注册) PTE_A
这个值是需要在kernel/riscv.h
中定义的,具体是第几位可以看上面关于PTE
中FLAGS
的图。- 计数后需要把页面设置为未访问,也就是把那一位设为
0
,与上掩码的反码就可以了。 - 为什么
ret
不是自加而是要这么运算呢,请注意要求返回的结果是bitmap
。
如果你中途出错的话,或许可以使用vmprint()
来 debug
。
最终成绩
最后记得 git add . && git commit -m "finish"
其实我觉得这部分 xv6
的教材讲的比较简略,可以看 《现代操作系统:原理与实现》 做进一步了解(不知道CSAPP
行不行,我还没看到那)