【xv6 源码窥探(2)】懒分配策略

前言

  • 本篇是关于 MIT 6.S081-2020-Lab5 的实现;
  • 如果内容上发现有什么问题请不要吝啬您的键盘。
Lazy allocation && Lazytests and Usertests 这两个练习的内容是紧耦合的,所以就写一块了。
Lazy allocation 的想法在之前在做页表实验的最后提到了,所以这次实验同样可以吃老本快速过关。
跟着提示做基本不会遇到问题,只有在最后有一个容易踩坑的地方。
先是修改 sys_sbrk 的实现:
/* kernel/sysproc.c */uint64 sys_sbrk(void) { int addr; int n; struct proc *p = myproc(); if(argint(0, &n) < 0) return -1; addr = p->sz; if(n < 0){ uvmdealloc(p->pagetable, addr, addr + n); }p->sz += n; return addr; }

再是处理 page-fault 异常中断:
/* kernel/trap.c */void usertrap(void) { ... } else if (r_scause() == 13 || r_scause() == 15) { uint64 va = r_stval(); uint64 sp = PGROUNDUP(p->trapframe->sp); if (va >= p->sz || va < sp) { p->killed = 1; } else { va = PGROUNDDOWN(va); char *pa = kalloc(); if(pa != 0){ memset(pa, 0, PGSIZE); if(mappages(p->pagetable, va, PGSIZE, (uint64)pa, PTE_W|PTE_X|PTE_R|PTE_U) != 0){ kfree(pa); } } else { p->killed = 1; } } } else if((which_dev = devintr()) != 0){ ... }

接着不要让 uvmcopyuvmunmap panic。
如果只是为了过 lazytests 和 usertests 的话,不用判断 va 是否是 heap 区域直接 continue 就可以直接过。
严谨点的话还是写一下比较好。
/* kernel/vm.c */void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { ... for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ if((pte = walk(pagetable, a, 0)) == 0) continue; // panic("uvmunmap: walk"); if((*pte & PTE_V) == 0) // panic("uvmunmap: not mapped"); continue; ... }int uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) { ... for(i = 0; i < sz; i += PGSIZE){ if((pte = walk(old, i, 0)) == 0) // panic("uvmcopy: pte should exist"); continue; if((*pte & PTE_V) == 0) continue; // panic("uvmcopy: page not present"); ... }

最后的最后,不要忘记处理这个 Hint:
  • Handle the case in which a process passes a valid address from sbrk() to a system call such as read or write, but the memory for that address has not yet been allocated.
起初我不太理解为什么要特别关照 read 和 write,难道它们看到缺页不会产生 page-fault 异常吗?
所以就头铁直接跑 usertests,结果挂了一个 sbrkarg 测试点。
找了找看是什么问题,发现存在一个调用链:sys_write->filewrite->writei->either_copyout->copyout->walkaddr
重点是在 walkaddr 函数内,如果发现一个 page 不存在或不可用,它只会返回 0,并不会产生 page-fault,也就不会被 usertrap 处理。
所以这里还要给 walkaddr 额外添加一个处理逻辑。
/* kernel/vm.c */// Look up a virtual address, return the physical address, // or 0 if not mapped. // Can only be used to look up user pages. uint64 walkaddr(pagetable_t pagetable, uint64 va) { pte_t *pte; uint64 pa; struct proc *p = myproc(); if(va >= MAXVA) return 0; pte = walk(pagetable, va, 0); if(pte == 0 || (*pte & PTE_V) == 0) { if (va >= p->sz || va < PGROUNDUP(p->trapframe->sp)) return 0; if ((pa = (uint64)kalloc()) == 0) return 0; if (mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, pa, PTE_W|PTE_X|PTE_R|PTE_U) != 0) { kfree((void*)pa); return 0; } return pa; } if((*pte & PTE_U) == 0) return 0; pa = PTE2PA(*pte); return pa; }

后记 【【xv6 源码窥探(2)】懒分配策略】多看

    推荐阅读