先说结论过了所有的测试点,我是能够过那个 usertests ,但是 lazytests 会一直卡在 out of mem 这个测试点,而且怎么也没法过,折腾了好久真的无语。

这个原因是因为 sbrk 是懒加载,我们直接用 addr+n 表示 proc 的 size。这样有个问题,xv6 最大的虚内存实际上是一个 MAXVA ,如果不加限制的话,walk 阶段就会 walk 到 MAXVA 导致一直 panic。解决办法其实很简单,在 uvmunmap 代码加个限定条件,强制 va 不能超过 MAXVA。

懒加载代码:

int lazy_alloc(uint64 addr){
  uint64 va = PGROUNDDOWN(addr);
  struct proc* p = myproc();
  if(addr >= p->sz || addr < p->trapframe->sp ){ //addr 如果大于 size 或者越栈了就报错
    return -1;
  }else {
    char* mem = kalloc();
    if(mem == 0){
      return -1;
    }else{ 
      memset(mem, 0, PGSIZE);
      if(mappages(p->pagetable,va ,PGSIZE , (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U)!=0){
        kfree(mem);
        uvmunmap(p->pagetable,va, 1, 1);
        return -1;
      }
    }
    return 0;
  }
  return -1;
}

usertrap 修改:

else if(r_scause() == 15 || r_scause() == 13){
    uint64 addr = r_stval();
    if(lazy_alloc(addr) != 0){
      printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
      printf("            sepc=%p stval=%p\n", r_sepc(), r_stval());
      p->killed = 1;
}

修改 uvmunmap 代码:

void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
 ...
  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
    if(a >=MAXVA)break; // 一定要加这个条件,a 不可能超过最大虚内存
    if((pte = walk(pagetable, a, 0)) == 0)
      /* panic("uvmunmap: walk"); */
      continue;
    if((*pte & PTE_V) == 0)
      /* panic("uvmunmap: not mapped"); */
      continue;
    ...
  }
}

walkaddr 修改,使得传入合法地址时候进行懒加载,讲道理这一步其实可以不用写的,我试了一下没有写这个 walkaddr 修改,照样也能跑通所有测试。证明了我的猜想,因为缺页中断发生在 load 等防存指令,如果地址不能被 walkaddr 说明发生了缺页,这时候硬件就会陷入 trap,然后进行处理。所以我们不在 walkaddr 中处理缺页也是没问题的,直接让 trap 来进行处理。但是 trap 是很贵的,每次我们要执行几百条甚至上千条指令,所以一个有效的解决办法就是 walkaddress 的时候也执行 lazy_alloc。而且有的函数比如 read、write 如果可能它没有在 walkaddress 中得到正确的 pa 可能也不会执行 trap 而是直接返回?因为不确定是否真的有这个页。。。所以还是在 walkaddress 这里吧 lazy_alloc 写一下:

if(pte == 0 || (*pte & PTE_V) ==0  ){
    if(lazy_alloc(va) != 0){
      return 0;
    }
    pte = walk(pagetable, va, 0);
  }

uvmcopy 修改,使得满足 fork:

if((pte = walk(old, i, 0)) == 0)
    /* panic("uvmcopy: pte should exist"); */
    continue;
if((*pte & PTE_V) == 0)
    /* panic("uvmcopy: page not present"); */
    continue;

最后就是 sbrk 代码了:

uint64
sys_sbrk(void)
{
  int addr;
  int n;
  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  if(n > 0){
    myproc()->sz = addr + n;
  }else{
    uvmdealloc(myproc()->pagetable, addr, addr +n);
    myproc()->sz = addr + n;
  }
    
  /* if(growproc(n) < 0) */
  /*   return -1; */
  return addr;
}

结果:

image-20220427163441467

这个 lab 虽然只是中等难度但是我写的时候很挣扎。。。。

更新于

请我喝[茶]~( ̄▽ ̄)~*

Kalice 微信支付

微信支付

Kalice 支付宝

支付宝