MIT xv6 2020系列实验:Lab5 lazy

这个和cow实验联系很紧密,并且cow比这个实验恶心一些的样子。

任务一:sbrk假分配

在sbrk中取消随栈指针变化对内存的分配/回收,只变化相应的栈指针。

这个比较简单,进入sbrk调用的growproc中去掉uvmalloc与uvmdealloc的部分即可。

任务二:Lazy allocation

提示:
13为读缺页,15为写缺页
r_stval() 返回寄存器 staval 的值, 该寄存器存储着引起缺页的虚拟地址
对于页面分配,你可以参考 growproc() 和 uvmalloc() . 你将会调用 kalloc() 和 mappages()使用 PGROUNDDOWN(va) 将出错的虚拟地址向下舍入到页面边界。
如果一个缺页发生在高于 sys_sbrk() 分配的虚拟地址,杀死这个进程只处理用户空间下的缺页错误

思路:
1.先处理缺页异常,在缺页异常中进行uvmalloc为页表项分配内存块
2.去除缺页异常
3.处理sbrk的负增长情况
4.其他事情

为缺页位置分配映射的物理页:

else if(r_scause() == 13 || r_scause() == 15){

    uint64 page_loc = r_stval();
    struct proc* p = myproc();
    
    if(p->sz <= page_loc || page_loc <= PGROUNDDOWN(p->trapframe->sp)){
      p->killed = 1;
    }
    else{
      // vmprint(p->pagetable);
      char *mem;
      page_loc = PGROUNDDOWN(page_loc);
      mem = kalloc();
      if(mem == 0) p->killed = 1;
      else{
        memset(mem, 0, PGSIZE);
        // printf("?ohmy %d",r_scause());
        if(mappages(p->pagetable, page_loc, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0)
          p->killed = 1;
        // printf("%x ?? %p %p %p\n",r_scause(),page_loc,(uint64)*walk(p->pagetable,page_loc,0),((uint64)*walk(p->pagetable,page_loc,0)&(uint64)(PTE_W)));
      }
    }
    // vmprint(p->pagetable);
  }

因为现在允许缺页,因此uvmunmap处的panic需要清除:

void
uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
{
  uint64 a;
  pte_t *pte;

  if((va % PGSIZE) != 0)
    panic("uvmunmap: not aligned");

  for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
    if((pte = walk(pagetable, a, 0)) == 0)
      continue;
    if((*pte & PTE_V) == 0)
      continue;
    if(PTE_FLAGS(*pte) == PTE_V)
      panic("uvmunmap: not a leaf");
    if(do_free){
      uint64 pa = PTE2PA(*pte);
      kfree((void*)pa);
    }
    *pte = 0;
  }
}

两处改为conitnue。

int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
  pte_t *pte;
  uint64 pa, i;
  uint flags;
  char *mem;

  for(i = 0; i < sz; i += PGSIZE){
    if((pte = walk(old, i, 0)) == 0)
      continue;
    if((*pte & PTE_V) == 0)
      continue;
    pa = PTE2PA(*pte);
    flags = PTE_FLAGS(*pte);
    if((mem = kalloc()) == 0)
      goto err;
    memmove(mem, (char*)pa, PGSIZE);
    if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
      kfree(mem);
      goto err;
    }
  }
  return 0;

 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  return -1;
}

uvmcopy处缺页也需要修改

处理sbrk负增长:

uint64
sys_sbrk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  struct proc *p = myproc();
  uint64 newaddr = addr+n;
  if(newaddr >= MAXVA || newaddr <= 0)
    return addr;
  
  if(n<0){
    if (uvmdealloc(p->pagetable, addr, newaddr) != newaddr)
      return -1;
  }
  p->sz = newaddr;
  return addr;
}

最后一个坑点是,因为只在usertrap中处理了缺页异常,如果是系统调用,那么这部分异常是没有办法处理的。
这部分缺页只在read,write系统调用中会出现,也就是copyout处,这里修改walkaddr的实现,在访问到未分配页的时候进行页表的一次映射:

uint64
walkaddr(pagetable_t pagetable, uint64 va)
{
  pte_t *pte;
  uint64 pa;

  if(va >= MAXVA)
    return 0;

  pte = walk(pagetable, va, 0);
  if(pte == 0 || (*pte & PTE_V) == 0)
  {
    struct proc *p = myproc();
    if (va >= p->sz || va < PGROUNDUP(p->trapframe->sp)) return 0;

    uint64 pa = (uint64)kalloc();
    if (pa == 0) return 0;

    if (mappages(p->pagetable, 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;
}

然后就可通过测试了。



posted @   Thaudmin  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示