6s081 lab copy on write 记录

6s081 lab copy on write 记录

本实验要求实现copy on write,先来考虑我们需要干些什么

  1. 修改fork,在生成子进程时不拷贝父进程内存,而是直接将复制父进程的页表内容到子进程的页表,同时把页表项设置为不可写入且为COW页表(在riscv.h中宏定义PTE_C);
  2. 在子进程或者父进程写入内存时产生pagefault,此时检查pte项是否有PTE_C标记,若有标记则分配内存,将COW页表内存拷贝到新分配的内存,并替换页表项,修改物理内存位置以及标记位为可写非COW页;
  3. 修改copyout,内容和2相似
  4. 考虑何时释放内存页,我们需要一个数组,记录每个页被引用次数page_ref,引用为1时若被kfree则执行释放操作,否则kfree时只page_ref–;同时kalloc时必定是内存第一次被分配,page_ref直接设为1。

那么做法就很明确了。

修改riscv.h

看这张图(图我盗的)
在这里插入图片描述
添加宏定义:

#define PTE_C (1L << 9)

修改kalloc.c

要修改4个部分

  1. 添加全局变量page_ref记录每页的引用次数
int page_ref[PHYSTOP/PGSIZE];
  1. 在kinit()里初始化page_ref为全1,为下一步kfree做准备,实际上这里可以被用户使用的内存没PHYSTOP/PGSIZE这么多,不过kernel的内存好像不会被free所以并不影响
void
kinit()
{
  for(int i = 0 ; i < PHYSTOP/PGSIZE; ++i)
    page_ref[i] = 1;
  initlock(&kmem.lock, "kmem");
  freerange(end, (void*)PHYSTOP);
}
  1. 修改kfree,维护page_ref[],用kfree和kalloc隐藏page_ref[]可以使大部分代码不需要修改,这个变量可以说“不存在”
void
kfree(void *pa)
{ 
  if( --page_ref[(uint64)pa/PGSIZE] > 0 )//加这句判断就行
    return;

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

  // 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);
}
  1. 修改kalloc(),分配时要把引用次数置为1
void *
kalloc(void)
{
  struct run *r;

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r){
    kmem.freelist = r->next;
    page_ref[(uint64)r/PGSIZE] = 1; //加这句
  }
  release(&kmem.lock);

  if(r)
    memset((char*)r, 5, PGSIZE); // fill with junk
  return (void*)r;
}

修改fork逻辑,在vm.c的uvmcopy.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;     //标记为cow页
    pa = PTE2PA(*pte);
    flags = PTE_FLAGS(*pte);

    // if((mem = kalloc()) == 0)
    //   goto err;
    // memmove(mem, (char*)pa, PGSIZE);
    if(mappages(new, i, PGSIZE, (uint64)pa, flags) != 0){
      goto err;
    }
    ++ page_ref[(uint64)pa/PGSIZE];//维护引用次数

  }
  return 0;

 err:
  uvmunmap(new, 0, i / PGSIZE, 1);
  //这里不需要动,释放还是调用的kfree,我们修改的kfree会
  //维护page_ref而不是直接free内存
  return -1;
}

修改usertrap()

遇到写入错误(15),判断pte是否有PTE_C标记,若有复制一页,修改标记,重新map

  else if( r_scause() == 15 ){

  	uint64 va = r_stval();
    pte_t *pte = walk( p->pagetable, va, 0);
    if(*pte & PTE_C){
      
      char* mem;
      uint64 flags, pa;
      pa = PTE2PA(*pte);

      if((mem = kalloc()) == 0){
        // printf("no mem remain on COW\n");
        p->killed = 1;
        exit(-1);
      }

      memmove(mem, (char*)pa, PGSIZE);
      flags = (PTE_FLAGS(*pte) | PTE_W) & (~PTE_C);
      uvmunmap(p->pagetable, PGROUNDDOWN(va), 1, 1);

      if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)mem, flags) != 0){
        kfree(mem);
        p->killed = 1;
        exit(-1);
      }

    }else{
      p->killed = 1;
      exit(-1);
    }
  }

修改copyout()

此部分逻辑个usertrap一致。

int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0, flags;
  pte_t *pte;
  char* mem;
  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0){
      return -1;
    }
    //从这开始加
    pte = walk(pagetable, va0, 0);
    if(*pte & PTE_C){
      if((mem = kalloc())==0){
        return -1;
      }

      memmove(mem, (char*)pa0, PGSIZE); 
      flags = ( PTE_FLAGS(*pte) | PTE_W ) & (~PTE_C);
      uvmunmap(pagetable, va0, 1, 1);

      if(mappages(pagetable, va0, PGSIZE, (uint64)mem, flags) != 0){
        kfree(mem);
        return -1;
      }
      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;
}

最后提醒,在defs.h里添加walk函数的原型
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值