MIT 6.S081 lab 6:Copy-on-Write Fork

1 Copy-on-Write Fork Lab

实现xv6 4.6节中的COW技术。

起始工作

// 1.增加标志位PTE_C的宏,表示是否为COW页
// riscv.h
#define PTE_C (1L << 8) // COW page flag

// 2.增添全局变量 pageCount 来对所有分配的物理页引用计数。
// kalloc.c
uint *pageCount[32];

初始化pageCount

  • 需要用32个页去计数从 KERBASE- PHYSTOP(这部分是可用于分配给进程的物理内存) 的所有页。
    • 注意:xv6 book上写的PHYSTOP 是0x86400000,而在代码里面是0x88000000
  • 另外写了一个分配函数count_kalloc() 去专门分配这32个页,因为原来的kalloc()函数中添加了引用计数部分的修改,所以需要在调用kalloc之前完成这32页的分配。
// kalloc.c
void
kinit()
{
  initlock(&kmem.lock, "kmem");
  freerange(end, (void*)PHYSTOP);
  for(int i = 0;i < 32;i++){
      pageCount[i] = count_kalloc();
      memset((char*)pageCount[i], 0, PGSIZE);
  }
}


void *
count_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

    return (void*)r;
}

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
  if(r){
    pageCount[GETPAGEOFF(r)][GETINTOFF(r)] = 1;
  }

  return (void*)r;
}

修改uvmcopy()

uvmcopy就是用在fork中去拷贝父进程的所有物理页,现在我们不需要去复制物理页,但是需要在这里做一些工作:

  • 修改标志位:父子进程的PTE_W都要置空,PTE_C都要置1
  • 增加物理页的引用计数
  • 删除复制物理内存部分的代码
// vm.c
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");
    *pte = *pte & ~PTE_W;
    *pte = *pte | PTE_C;
    pa = PTE2PA(*pte);
    // increment the page count
    pageCount[GETPAGEOFF(pa)][GETINTOFF(pa)]++;

      flags = PTE_FLAGS(*pte);
//    if((mem = kalloc()) == 0)
//      goto err;
//    memmove(mem, (char*)pa, PGSIZE);
    if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){
//      kfree(mem);
      goto err;
    }
  }
  return 0;

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

修改 usertrap.c

跟LazyAlloc差不多,主要的处理逻辑就在trap里面,内容也很像。

  • 先判断是否是COW引起的缺页
  • 如果是的话,复制一份新的物理页 ,并且修改pte的内容,包括指向的物理页,以及标志位。
  • 减少引用计数,如果为0,就释放。
// trap.c
else {
      uint64 va = r_stval();
      if((r_scause() != 13 && r_scause() != 15) || va >= p->sz){
          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;
      }else{
          pte_t *pte = walk(p->pagetable,va,0);
          if((*pte & PTE_C) && !(*pte & PTE_W)){
              uint64 pa = PTE2PA(*pte);
              char* mem = (char *)kalloc();
              if(mem == 0){
                  p->killed = 1;
              }else{
                  memmove(mem, (char*)(pa), PGSIZE);

                  // modify the flag bit
                  int flag = PTE_FLAGS(*pte);
                  flag = (flag | PTE_W) & ~PTE_C;
                  *pte = PA2PTE(mem) | flag;

                  // when the reference become zero ,free it
                  if(--pageCount[GETPAGEOFF(pa)][GETINTOFF(pa)] == 0){
                      kfree((void *)pa);
                  }
              }
          }
          else{
              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;
          }
      }

  }

修改copyout()

一开始还蛮疑惑为啥要改copyout,因为copyout是在kernel态进行的,缺页不会进入trap,所以需要在这边重新写一遍 trap的部分处理逻辑。

这里注意使用的页表应该是传入参数,而不是进程页表。一开始直接复制的 trap,然后debug de了贼旧~~

传入参数的话,主要是处理,exec新建页表的情况。

//vm.c
int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;
    
  // 有个测试点需要用到
  if(dstva > MAXVA)
  	return -1;

  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    pte_t  *pte = walk(pagetable,va0,0);
    pa0 = walkaddr(pagetable, va0);
 	if(pa0 == 0)
      return -1;
    // If cow page
      if((*pte & PTE_C) && !(*pte & PTE_W)){

          char* mem = (char *)kalloc();
          if(mem == 0){
              return -1;
          }else{
              memmove(mem, (char*)(pa0), PGSIZE);

              // modify the flag bit
              int flag = PTE_FLAGS(*pte);
              flag = (flag | PTE_W) & ~PTE_C;
              *pte = PA2PTE(mem) | flag;

              // when the reference become zero ,free it
              if(--pageCount[GETPAGEOFF(pa0)][GETINTOFF(pa0)] == 0){
                  kfree((void *)pa0);
              }
          }
          pa0 = (uint64 )mem;
      }


   
    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;
}

修改uvmunmap()

现在对于物理页的删除,我们是依靠引用计数为0的条件,所以需要修改uvmunmap的显式删除。

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)
      panic("uvmunmap: walk");
    if((*pte & PTE_V) == 0)
      panic("uvmunmap: not mapped");
    if(PTE_FLAGS(*pte) == PTE_V)
      panic("uvmunmap: not a leaf");
    if(do_free){
      uint64 pa = PTE2PA(*pte);
      if(--pageCount[GETPAGEOFF(pa)][GETINTOFF(pa)] == 0)
      kfree((void*)pa);
    }
    *pte = 0;
  }
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值