MIT6.S081Lab6:Copy on Write Fork for xv6

Copy-on-Write Fork for xv6

这个实验本身并不难,思路还是很清晰的,但是我想的有点多了。首先我纠结得最久的就是索引数组在哪里声明以及索引数组的大小。开始我想索引数组在哪里声明才能让所有进程都能访问到,如果声明在某个函数里面则随着函数的结束空间就释放了,所以我想申请在堆里,但是不切实际,毕竟malloc函数都没有,而且这个数组是关于内核本身的。最后才发现其实直接在某个文件里面定义成全局变量,就自动是放在内核的BSS段或者DATA段,其他文件中用extern声明访问即可。

然后是索引数组的大小了,开始我想极致地缩小数组大小,所以我想的是PHYSTOP-end,这个end就是kalloc.c中的那个end,后来我发现其他文件很难访问到这个end的大小,于是我想了一些其他的办法很复杂,于是干脆用PHYSTOP-0发现也行。

其他的就也要注意一些细节吧。kalloc的时候把物理地址对应的索引数组置为1,kfree的时候先减1,然后判断是否大于0来决定要不要释放页面。

// kalloc
void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r)
    kmem.freelist = r->next;
  release(&kmem.lock);

  if(r) {
    memset((char*)r, 5, PGSIZE); // fill with junk
    pagearray[(uint64)r/PGSIZE] = 1;
  }
  return (void*)r;
}
// kfree
void
kfree(void *pa)
{
  struct run *r;

  if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
    panic("kfree");

  pagearray[(uint64)pa/PGSIZE]--;
  if (pagearray[(uint64)pa/PGSIZE] > 0) {
    return;
  }

  // Fill with junk to catch dangling refs.
  memset(pa, 1, PGSIZE);

  r = (struct run*)pa;

  acquire(&kmem.lock);
  r->next = kmem.freelist;
  kmem.freelist = r;
  release(&kmem.lock);
}

然后是uvmcopy函数,在这个里面不分配页面了,直接让新页表指向原来的物理地址并且更改两个PTE的flag,改为非写并且用CSW位来标识是cow对应PTE(其实不用CSW这个实验也能完成)。

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)
      panic("uvmcopy: pte should exist");
    if((*pte & PTE_V) == 0)
      panic("uvmcopy: page not present");
    pa = PTE2PA(*pte);
    (*pte) = (*pte) & (~PTE_W);
    (*pte) = (*pte) | (PTE_CSW);
    flags = PTE_FLAGS(*pte);
    if(mappages(new, i, PGSIZE, pa, flags) != 0){
      goto err;
    }
    pagearray[(uint64)pa/PGSIZE]++;
  }
  return 0;

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

然后是写一个函数用来处理页面因为cow导致的页面错误,就是判断PTE是否符合要求,分配页面,复制页面,尝试释放原来的页面,更改PTEflag,将PTE与物理地址map上。

int
cowpage(pagetable_t pagetable, uint64 va) 
{
  if (va > MAXVA) {
    return -1;
  }
  va = PGROUNDDOWN(va);
  pte_t *pte = walk(pagetable, va, 0);
  if (pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0) {
    return -1;
  }
  if (((*pte) & PTE_CSW) == 0) {
    return -1;
  }

  char *mem = kalloc();
  if (mem == 0) {
    return -1;
  }
  uint64 pa = PTE2PA(*pte);
  uint flags = PTE_FLAGS(*pte);
  flags |= PTE_W;
  flags &= (~PTE_CSW);
  memmove(mem, (char*)pa, PGSIZE);
  uvmunmap(pagetable, va, 1, 1);
  // (*pte) = 0;
  // pagearray[pa/PGSIZE]--;
  // kfree((void*)pa);
  // *pte = PA2PTE(mem) | flags;
  if(mappages(pagetable, va, PGSIZE, (uint64)mem, flags) != 0) {
    kfree(mem);
    return -1;
  }
  return 0;
}

然后在usertrap和copyout中调用这个函数

//usertrap
else if (r_scause() == 15) {
    uint64 va = r_stval();
    if (cowpage(p->pagetable, va) != 0) {
      p->killed = 1;
    }
  }
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;

  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    if (va0 > MAXVA) {
      return -1;
    }
    pte_t *pte = walk(pagetable, va0, 0);
    if (pte == 0)
      return -1;
    if (((*pte) & PTE_W) == 0) {
      if (cowpage(pagetable, va0) != 0)
        return -1;
    }
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    n = PGSIZE - (dstva - va0);
    if(n > len)
      n = len;
    memmove((void *)(pa0 + (dstva - va0)), src, n);

    len -= n;
    src += n;
    dstva = va0 + PGSIZE;
  }
  return 0;
}

在defs.h中加上相应定义。

最后运行cowtestusertests通过。

总结

实验不难,但是要仔细,而且很多时候,当你觉得你的想法实现太复杂了,多半是想法有问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值