开学!
git
1 2 3
| git fetch git checkout pgtbl make clean
|
Speed up system calls
为了优化 getpid 系统调用,不用每次进入内核态获取 PID,创建一个用户可读的页,将 USYSCALL 映射到该页上,在调用 ugetpid 时,直接访问 USYSCALL 即可拿到 pid,不需要系统调用,算是以空间换时间
这里不同的是,页表映射实际上是虚拟地址映射到虚拟地址,而不是映射到物理地址
- 在
kernel/proc.h
中 proc 结构体加入 struct usyscall *usyscall
- 在
allocproc
初始化 usyscall
kernel/proc.c1 2 3 4 5 6 7 8 9 10 11 12 13
| static struct proc* allocproc(void) { ...
if((p->usyscall = (struct usyscall *)kalloc()) == 0){ freeproc(p); release(&p->lock); return 0; } p->usyscall->pid = p->pid;
... }
|
- 在
proc_pagetable
建立映射
kernel/proc.c1 2 3 4 5 6 7 8 9 10 11 12 13 14
| pagetable_t proc_pagetable(struct proc *p) { ...
if(mappages(pagetable, USYSCALL, PGSIZE, (uint64)(p->usyscall), PTE_R | PTE_U) < 0){ uvmunmap(pagetable, TRAMPOLINE, 1, 0); uvmunmap(pagetable, TRAPFRAME, 1, 0); uvmfree(pagetable, 0); return 0; }
... }
|
- 在 freeproc 释放 usyscall
kernel/proc.c1 2 3 4 5 6 7 8 9 10
| static void freeproc(struct proc *p) { ...
if(p->usyscall) kfree((void*)p->usyscall); p->usyscall = 0;
... }
|
Print a page table
xv6 使用三级页表,在运行第一个用户进程时打印出其页表
这里使用一个静态变量 level 表示在第几级页表
kernel/vm.c1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void vmprint(pagetable_t pagetable) { static int level = 0; if(level == 0) { level++; printf("page table %p\n", pagetable); }
for(int i = 0; i < 512 && level <= 3; ++i) { pte_t pte = pagetable[i];
if(pte & PTE_V) { for(int j = 0; j < level; ++j) { printf(" .."); } printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte)); level++; vmprint((pagetable_t)PTE2PA(pte)); level--; } }
if(level == 1) { level--; } }
|
看到 wingsgg 用 pte & (PTE_R|PTE_W|PTE_X) == 0
表示是否中间页表,确实很棒,但是用了两个函数
笔者已经用一个函数写好了,觉得也还行
然后在 exec.c 和 defs.h 中添加声明和使用就行
Detect which pages have been accessed
RISC-V 硬件会在 TLB 命中失败时,将对应 PTE 的 Access 标志位设 1,用来记录该页面有没有访问过
写一个系统调用,三个参数,检测的虚拟地址,检测的页数,bitmask
挺简单的,不知道为什么实验难度写着 hard
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #define PTE_A (1L << 6)
int sys_pgaccess(void) { uint64 buf; int n; uint64 uabits; argaddr(0, &buf); argint(1, &n); argaddr(2, &uabits); uint abits = 0; pagetable_t pagetable = myproc()->pagetable; for(int i = 0; i < n; ++i) { pte_t *pte; pte = walk(pagetable, buf + i * PGSIZE, 0); if(!pte) { return -1; } if(*pte & PTE_A) { abits |= 1 << i; *pte &= ~PTE_A; } } copyout(pagetable, uabits, (char*)&abits, sizeof(abits)); return 0; }
|
注意检测完后,将标记置零,不然不知道检测后还没有访问过
Optional challenge exercises
使用 super-pages 减少页表中 PTE 的数量
不是很懂,改用更大的页(?)
取消用户进程的第一页的映射,
这样可以使引用空指针直接造成错误
需要修改 user.ld 文件,让进程的 text 段从 0x1000 开始,而不是 0
估计要改很多东西(uvmmap,uvmalloc啥的)。。。咕咕咕
添加一个系统调用报告 dirty pages(修改过的页表)
和第三个差不多,就不做了