开学!
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(修改过的页表)
和第三个差不多,就不做了