开学!
git
1 2 3
| git fetch git checkout pgtbl make clean
|
Speed up system calls
为了优化 getpid
系统调用,不用每次进入内核态获取 PID,创建一个用户可读的页,将 USYSCALL 映射到该页上
可以观察 ugetpid
函数的定义,它直接访问 USYSCALL 即可拿到 pid,不需要系统调用,算是以空间换时间
user/ulib.c1 2 3 4 5 6
| int ugetpid(void) { struct usyscall *u = (struct usyscall *)USYSCALL; return u->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, TRAPFRAME, 1, 0); uvmunmap(pagetable, TRAMPOLINE, 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;
... }
|
- 在
proc_freepagetable
取消页面映射(这里实验文档没说,要自己发现在 freeproc
函数中调用了这个函数)
kernel/proc.c1 2 3 4 5 6 7 8
| void proc_freepagetable(pagetable_t pagetable, uint64 sz) { uvmunmap(pagetable, TRAMPOLINE, 1, 0); uvmunmap(pagetable, TRAPFRAME, 1, 0); uvmunmap(pagetable, USYSCALL, 1, 0); uvmfree(pagetable, sz); }
|
提问:还有什么其他的系统调用可以通过这样的共享页来加快速度?
怎么感觉没有了
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
| void pteprint(pagetable_t pagetable, int level) { for(int i = 0; i < 512; i++) { pte_t pte = pagetable[i]; if(pte & PTE_V) { uint64 child = PTE2PA(pte); for(int j = 0; j < level; j++) printf(" .."); printf("%d: pte %p pa %p\n", i, pte, child); if((pte & (PTE_R | PTE_W | PTE_X)) == 0) pteprint((pagetable_t)child, level+1); } } }
void vmprint(pagetable_t pagetable) { printf("page table %p\n", pagetable); pteprint(pagetable, 1); }
|
笔者之前使用局部静态变量来判断 level,但是想着如果是多线程的话没有加锁可能会出问题
然后在 defs.h 和 exec.c 中添加声明和使用就行
Detect which pages have been accessed
RISC-V 硬件会在 TLB 命中失败时,将对应 PTE 的 Access 标志位设 1,用来记录该页面有没有访问过
写一个系统调用,三个参数,检测的地址,检测的页数,bitmask
挺简单的,不知道为什么实验难度写着 hard
kernel/sysproc.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 26 27 28 29 30 31 32 33 34 35
| #define PTE_A (1L << 6)
int sys_pgaccess(void) { uint64 base; uint64 mask; int len; unsigned int abits;
argaddr(0, &base); argint(1, &len); if(len > 32) return -1; argaddr(2, &mask);
abits = 0; pagetable_t pagetable = myproc()->pagetable;
for(int i = 0; i < len; i++) { pte_t *pte = walk(pagetable, base + PGSIZE * i, 0); if(pte == 0) return -1;
if(*pte & PTE_A) { abits |= 1 << i; *pte &= ~PTE_A; } }
if(copyout(pagetable, mask, (char*)&abits, sizeof(abits)) < 0) return -1;
return 0;`` }
|
注意检测完后,将标记置零,不然不知道检测后还没有访问过
Optional challenge exercises
使用 super-pages 减少页表中 PTE 的数量
不是很懂,改用更大的页(?)
取消用户进程的第一页的映射,
这样可以使引用空指针直接造成错误
需要修改 user.ld 文件,让进程的 text 段从 0x1000 开始,而不是 0
估计要改很多东西(uvmmap,uvmalloc啥的)。。。咕咕咕
添加一个系统调用报告 dirty pages(修改过的页表)
和第三个差不多,就不做了