文章

MIT 6.S081 | Lab5: xv6 lazy page allocation

课程地址:https://pdos.csail.mit.edu/6.S081/2020/schedule.html 代码地址:https://github.com/36-H/xv6-labs-2020/tree/lazy

Lab5: xv6 lazy page allocation

Eliminate allocation from sbrk() (easy)

YOUR JOB 你的首项任务是删除sbrk(n)系统调用中的页面分配代码(位于sysproc.c中的函数sys_sbrk())。sbrk(n)系统调用将进程的内存大小增加n个字节,然后返回新分配区域的开始部分(即旧的大小)。新的sbrk(n)应该只将进程的大小(myproc()->sz)增加n,然后返回旧的大小。它不应该分配内存——因此您应该删除对growproc()的调用(但是您仍然需要增加进程的大小!)。

// kernel/sysproc.c

uint64
sys_sbrk(void)
{
  int addr;
  int n;
  struct proc *p = myproc();
  if(argint(0, &n) < 0)
    return -1;
  addr = p->sz;
  // if(growproc(n) < 0)
  //   return -1;
  if(n < 0){
    uvmdealloc(p->pagetable, p->sz, p->sz+n);
  }
  // lazy
  p->sz += n;
  return addr;
}

Lazy allocation (moderate) and Lazytests and Usertests (moderate)

//kernel/vm.c
//懒分配内存
void
uvmlazyalloc(uint64 va){
  struct proc *p = myproc();
  char *mem = kalloc();
  if(mem == 0){
    printf("lazy alloc error: out of memory\n");
    p->killed = 1;
    return;
  }
  memset(mem, 0, PGSIZE);
  //开始映射内存 将虚拟地址 va 所在页面的起始地址映射到物理内存地址 mem,
  //并设置页面权限为可写、可执行、可读和用户模式访问
  if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
    printf("lazy alloc error: failed to map page\n");
    kfree(mem);
    p->killed = 1;
  }
}

int
uvmshouldbealloc(uint64 va){
  pte_t *pte;
  struct proc *p = myproc();
  return (va < p->sz //缺失映射的地址是否小于进程申请的地址
      && PGROUNDDOWN(va) != r_sp() //地址不应该是guard page
      && (((pte = walk(p->pagetable, va, 0))==0) || ((*pte & PTE_V)==0))); //判断页表项是否有效
}
//
// handle an interrupt, exception, or system call from user space.
// called from trampoline.S
//
void
usertrap(void)
{
  int which_dev = 0;
  if((r_sstatus() & SSTATUS_SPP) != 0)
    panic("usertrap: not from user mode");
  // send interrupts and exceptions to kerneltrap(),
  // since we're now in the kernel.
  w_stvec((uint64)kernelvec);
  struct proc *p = myproc();
  
  // save user program counter.
  p->trapframe->epc = r_sepc();
  
  if(r_scause() == 8){
    // system call
    if(p->killed)
      exit(-1);
    // sepc points to the ecall instruction,
    // but we want to return to the next instruction.
    p->trapframe->epc += 4;
    // an interrupt will change sstatus &c registers,
    // so don't enable until done with those registers.
    intr_on();
    syscall();
  } else if((which_dev = devintr()) != 0){
    // ok
  } else {
  	//这里进行检查,判断该错误是否为页面错误
    uint64 va = r_stval();
    if((r_scause() == 13 || r_scause() == 15) &&
      uvmshouldbealloc(va)
    ){
      uvmlazyalloc(va);
    }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;
    }
  }

  if(p->killed)
    exit(-1);
  // give up the CPU if this is a timer interrupt.
  if(which_dev == 2)
    yield();
  usertrapret();
}

修改页面缺少映射,带来的问题:

//kernel/vm.c
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");
      continue;
    if((*pte & PTE_V) == 0)
      // panic("uvmunmap: not mapped");
      continue;
    if(PTE_FLAGS(*pte) == PTE_V)
      panic("uvmunmap: not a leaf");
    if(do_free){
      uint64 pa = PTE2PA(*pte);
      kfree((void*)pa);
    }
    *pte = 0;
  }
}


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");
      continue;
    if((*pte & PTE_V) == 0)
      // panic("uvmcopy: page not present");
      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;
}


int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
  uint64 n, va0, pa0;

  //here
  if(uvmshouldbealloc(srcva)){
    uvmlazyalloc(srcva);
  }

  while(len > 0){
    va0 = PGROUNDDOWN(srcva);
    pa0 = walkaddr(pagetable, va0);
    if(pa0 == 0)
      return -1;
    n = PGSIZE - (srcva - va0);
    if(n > len)
      n = len;
    memmove(dst, (void *)(pa0 + (srcva - va0)), n);

    len -= n;
    dst += n;
    srcva = va0 + PGSIZE;
  }
  return 0;
}

int
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
{
  uint64 n, va0, pa0;
  
  //here
  if(uvmshouldbealloc(dstva)){
    uvmlazyalloc(dstva);
  }

  while(len > 0){
    va0 = PGROUNDDOWN(dstva);
    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;
}
License:  CC BY 4.0