MIT6.s081 lab cow

LAB:cow

1.实验概述

本次实验的内容为给xv6实现一个cow功能,具体情况为,在fork时,子进程一般会立即复制父进程的内存并获取一段新的内存,但这些内存并不是会立即使用的,所谓我们要实现的功能为,在fork时,不给子进程分配内存,而是直接将子进程的pagetable和父进程映射同一段物理内存,随后当需要修改这段内存时,触发一个pagefault,在中断处理程序中分配内存。

2.具体实现

首先修改uvmcopy函数,为了在后续修改内存时触发pagetable,我们需要将父进程和子进程的pagetable上的对应ptes标记位PTE_W去除并添加PTE_C

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

  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_W;
    *pte |= PTE_C;
    flags = PTE_FLAGS(*pte);
    if(mappages(new, i, PGSIZE, pa, flags) != 0)
      goto err;
    addpagecount((void *)pa, 1);
  }
  return 0;

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

usertrap中分配新内存,并复制原物理地址中的内容到其中,修改pagetable上的对应ptes,同时尝试释放原物理地址(因为根据物理地址无法检索到映射它的两个虚拟地址,例如有一个父进程和一个子进程同时映射一块物理地址,两个进程访问到那块PTE_W标记位为0的物理块时,都会复制出一块新内存并尝试释放这个物理块)。

// usertrap
else if(r_scause() == 15){
	if (cowhandler() == -1) {
		p->killed = 1;
	}
}

int
cowhandler() {
  struct proc *p = myproc();
  uint64 va = r_stval();
  if (va > MAXVA) 
    return -1;

  pte_t *pte = walk(p->pagetable, va, 0);
  if (*pte & PTE_C) {
    uint64 pa = PTE2PA(*pte);
    uint64 ka = (uint64)kalloc();
    if (ka == 0) {
      return -1;
    } else {
      memmove((char *)ka, (char *)pa, PGSIZE);
      kfree((void *)pa);
      uint flags = PTE_FLAGS(*pte);
      *pte = PA2PTE(ka) | flags | PTE_W;
      *pte &= ~PTE_C;
    }
  }
  return 0;
}

现在需要处理kalloc.c中的函数

int page_count[PHYSTOP/PGSIZE];
struct spinlock pc_lock;

void
kinit()
{
  initlock(&kmem.lock, "kmem");
  initlock(&pc_lock, "pc_lock");
  freerange(end, (void*)PHYSTOP);
}

void
freerange(void *pa_start, void *pa_end)
{
  char *p;
  p = (char*)PGROUNDUP((uint64)pa_start);
  for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) {
    page_count[PA2INDEX(p)] = 1;
    kfree(p);
  }
}

void
kfree(void *pa)
{
  struct run *r;

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

  acquire(&pc_lock);
  if (page_count[PA2INDEX(pa)] < 1)
    panic("kfree");
  page_count[PA2INDEX(pa)] -= 1;
  int tmp = page_count[PA2INDEX(pa)];
  release(&pc_lock);


  if (tmp == 0) {
    // 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);
  }
}

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

  acquire(&kmem.lock);
  r = kmem.freelist;
  if(r) {
    kmem.freelist = r->next;
    acquire(&pc_lock);
    page_count[PA2INDEX((void *)r)] = 1;
    release(&pc_lock);
  }
  release(&kmem.lock);
   
  if(r)
    memset((char*)r, 5, PGSIZE); // fill with junk
  return (void*)r;
}

int
addpagecount(void *pa, int n)
{
  acquire(&pc_lock);
  page_count[PA2INDEX(pa)] += n;
  release(&pc_lock);
}
posted @   silly19  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示