Lab lazy page allocation

一 实验原理

        当进程调用sbrk()分配地址空间时,会从用户态进入到内核态,通过trap handler程序完成系统调用,并通过sys_sbrk()为用户分配虚拟地址和物理地址。分配物理地址,会加大操作系统的开销,而且为进程分配的物理地址未必会被使用到。一种提高地址分配效率的办法是在一开始并不为进程分配物理地址,当进程需要访问虚拟地址时,触发缺页中断,内核在处理缺页中断的时候为进程分配物理地址。这种结合缺页中断分配地址的策略,虽然可以减少分配物理地址所带来的开销,但是另一方面会增加用户态与内核态之间切换带来的开销。

二 实验部分

这个lab实现lazy page allocation的策略。

1 在kernel/trap.c的usertrap函数中,当scause寄存器的值为13或者15时表明此时是缺页中断,通过stval寄存器获得造成缺页中断的虚拟地址,在保证缺页中断的地址合理的情况下,为其分配物理地址。

else if(r_scause()==13||r_scause()==15){
    uint64 va=r_stval();
    if(va>p->sz||PGROUNDUP(va)==PGROUNDDOWN(p->trapframe->sp)) exit(-1);
    char *mem=kalloc();
    if(mem==0)
      exit(-1);
    if(mappages(p->pagetable,PGROUNDDOWN(va),PGSIZE,(uint64)mem,PTE_R|PTE_W|PTE_X|PTE_U)!=0){
      kfree(mem);
      exit(-1);
    }

  } 

2 在kernel/sysproc.c的sys_sbrk函数中,当n为正值时,不分配物理地址,只是update p->sz的值,当n为负值时,释放地址,最终返回进程的old size。

uint64
sys_sbrk(void)
{
  int addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  if(n>0)
    myproc()->sz+=n;
  else if(growproc(n)<0)
    return -1;
  return addr;
}

3 在kernel/vm.c的uvmunmap函数中,进程需要释放物理页时,可能会释放还没有分配的虚拟地址。同理fork会调用uvmcopy为子进程复制父进程的内存,而父进程可能存在还未被分配的地址。

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

4 当进程调用write pipe 和read去解引用user space的地址时,可能会解引用还未被分配的地址。而此时因为系统调用,进程已经从用户态进入到了内核态,发生缺页中断时会执行kerneltrap而并不是usertrap,因此需要为这三种情况分配地址。allocmem函数传入的参数是页表、虚拟地址以及需要分配的地址的大小,若虚拟地址合理且没有分配相应的物理地址,则为其分配地址。然后在sys_write sys_read 和 sys_pipe的合适地方调用该函数即可。

kernel/sysfile.c

uint64
allocmem(pagetable_t pgtbl,uint64 addr,int n){
   uint64 va=PGROUNDDOWN(addr),pa;
   if(addr>myproc()->sz||addr+n>myproc()->sz)
     return -1;
   int nop=(PGROUNDDOWN(addr+n)-va)/PGSIZE;
   if(nop==0) nop=1;
   for(int i=0;i<nop;i++,va+=PGSIZE){
     if((pa=walkaddr(pgtbl,va))==0){
       char *mem=kalloc();
       if(mem==0){
         uvmunmap(pgtbl,PGROUNDDOWN(addr),i,1);
	     return -1;
       }
       memset(mem,0,PGSIZE);
       pa=(uint64)mem;
       if(mappages(pgtbl,va,PGSIZE,pa,PTE_R|PTE_W|PTE_X|PTE_U)!=0){
         kfree(mem);
         uvmunmap(pgtbl,PGROUNDDOWN(addr),i,1);
	     return -1;
       }
     }
   }
   return 0;
}

//sys_read
uint64
sys_read(void)
{
  struct file *f;
  int n;
  uint64 p;

  if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0)
    return -1;
  if(allocmem(myproc()->pagetable,p,n)<0)
    return -1;

  return fileread(f, p, n);
}

//sys_write
uint64
sys_write(void)
{
  struct file *f;
  int n;
  uint64 p;

  if(argfd(0, 0, &f) < 0 || argint(2, &n) < 0 || argaddr(1, &p) < 0)
    return -1;

  if(allocmem(myproc()->pagetable,p,n)<0)
    return -1;
  return filewrite(f, p, n);
}

//sys_pipe
uint64
sys_pipe(void)
{
  uint64 fdarray; // user pointer to array of two integers
  struct file *rf, *wf;
  int fd0, fd1;
  struct proc *p = myproc();

  if(argaddr(0, &fdarray) < 0)
    return -1;
  if(pipealloc(&rf, &wf) < 0)
    return -1;
  fd0 = -1;
  if((fd0 = fdalloc(rf)) < 0 || (fd1 = fdalloc(wf)) < 0){
    if(fd0 >= 0)
      p->ofile[fd0] = 0;
    fileclose(rf);
    fileclose(wf);
    return -1;
  }
  allocmem(p->pagetable,fdarray,sizeof(fd0)+sizeof(fd1));
  if(copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||
     copyout(p->pagetable, fdarray+sizeof(fd0), (char *)&fd1, sizeof(fd1)) < 0){
    p->ofile[fd0] = 0;
    p->ofile[fd1] = 0;
    fileclose(rf);
    fileclose(wf);
    return -1;
  }
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值