MIT6828 学习笔记 011 (lab pgtbl)
1. 给系统调用加速
一些操作系统(如 Linux )通过在一块只读区域和用户空间共享数据来为某些系统调用加速。现在给 Xv6 的 getpid 系统调用加速:当进程被创建时,在 USYSCALL (kernel/memlayout.h:74) 处映射一个只读分页,在这个分页的起点保存一个 struct usyscall (kernel/memlayout:76) ,把 pid 放进去。
思路:要给新创建的进程映射新的分页表,先看看创建进程的系统调用的实现,发现 sys_fork 在 kernel/sysproc.c:24 ,它直接调用了 fork (kernel/proc.c:279) ,里面用 allocproc (kernel/proc.c:109) 分配新的进程,其中 proc_pagetable (kernel/proc.c:176) 用来创建分页表,只要在 proc_pagetable 里映射 USYSCALL 就可以了:
点击展开:我的答案
1 | diff --git a/kernel/proc.c b/kernel/proc.c |
问:还有那些 Xv6 系统调用可以利用这个共享分页实现提速?并解释原因。答:别的我不敢确定,但
uptime系统调用应该可以通过这个方法提速,因为进程只需要读取ticks(kernel/trap.c:10) ,内核更新这个数据也只需要写入ticks,用户进行uptime调用后不需要进入内核,直接读取数据即可。
2. 打印一个分页表
定义一个 vmprint(pagetable_t pgtbl) 函数,放在 exec (kernel/exec.c:22) 的 return 之前,打印出第一个进程的分页表(声明在 kernel/defs.h ,定义在 kernel/vm.c )
点击展开:我的答案
1 | diff --git a/kernel/defs.h b/kernel/defs.h |
我的一次运行结果:
1 | qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp |
%p具体怎么打印地址是编译器实现的,没有统一标准,我的测试下 x86_64 的 GCC 和 Clang 都输出0x开头的 48-bit 的地址,而 x86_64 的 MinGW GCC 则输出没有0x开头的 64-bit 的地址。- PTE 的 X, W, R flag 的意义: RISC-V Privilleged Table 4.5
3. 检测哪些分页被访问过
RISC-V 会在 TLB (Translation Look-aside Buffer) 未命中时设置分页表的 A flag ,实现函数 pgaccess() ,第一个参数是分页表起点地址,第二个参数是要检测的分页表的数量,第三个参数用于接收结果,从低 bit 到高 bit 保存从低地址到高地址的分页表的 A flag
点击展开:我的答案
1 | diff --git a/kernel/riscv.h b/kernel/riscv.h |
RISC-V 手册说修改这些 flag 需要保证原子操作,但我是个铸币,没找到能用的机制/锁,所以干脆就没管,反正测试能过