xv6-lab5-lazy

Lab:xv6 lazy page allocation

Eliminate allocation from sbrk()

实验目标

本实验你的任务是修改 sys_sbrk() 使得其只增加/减少进程地址空间大小,而不真正地分配页面

实验实现

/*	sysproc.c	*/
uint64
sys_sbrk(void)
{
  uint64 addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  uint64 newaddr = addr + n;
  if(newaddr >= MAXVA || newaddr <= 0)
    return addr;

  // if(n < 0)
  // {
  //   if (uvmdealloc(myproc()->pagetable, addr, newaddr) != newaddr)
  //     return -1;
  // }
  
  myproc()->sz = newaddr;
  return addr;
}

Lazy allocation + Lazytests and Usertests

这里建议两个一起做了,免得回头还要修改

实验目标

本实验中,你的目标是修改trap.c和其他地方的代码,使得在你的代码能够在缺页时分配一块新的内存并建立映射

实验实现

先看提示

  • 你可以用 r_scause() 来查看错误类型,13为读缺页,15为写缺页
  • r_stval() 返回寄存器 staval 的值, 该寄存器存储着引起缺页的虚拟地址
  • 对于页面分配,你可以参考 growproc()uvmalloc() . 你将会调用 kalloc()mappages()
  • 使用 PGROUNDDOWN(va) 将出错的虚拟地址向下舍入到页面边界。
  • 如果一个缺页发生在高于 sys_sbrk() 分配的虚拟地址,杀死这个进程
  • 只处理用户空间下的缺页错误

根据以上提示,我们可以先完成trap.c 中缺页处理这部分的代码

主要思路如下

  1. 先检查是否为缺页错误

  2. 检查

  3. 若为缺页错误则使用 r_stval() 获取虚拟地址并检查是否越界

  4. 仿照 uvmalloc() 分配内存并建立虚拟地址到内存的映射

/*	trap.c	*/
void
usertrap(void)
{

  if(r_scause() == 8){
	......
  } else if(r_scause() == 13 || r_scause() == 15){
    uint64 va = r_stval();
    if(va >= p->sz || va <= PGROUNDDOWN(p->trapframe->sp))  p->killed = 1;
    else
    {
      uint64 pa = (uint64)kalloc();
        
/*
	此处踩坑!!!!!!!!!!!!!!!!!!!
	误把p->killed = 1当return 用了
	直接
    if(pa == 0) p->killed = 1;
    memset((void *)pa, 0, PGSIZE);
    if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)pa, PTE_W|PTE_X|PTE_R|PTE_U) != 0) 
        p->killed = 1;
    导致有的内存没分配到,下面的memset出错,好久没找到。。。
*/
      if(pa == 0) p->killed = 1;
      else
      {
        memset((void *)pa, 0, PGSIZE);
        if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)pa, PTE_W|PTE_X|PTE_R|PTE_U) != 0) 
          p->killed = 1;
      }
    }
  }......
}

继续看提示,还有各种小情况要处理

  • 修改uvmunmap() ,使它不会在虚拟地址未映射的情况下报错
/*	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)
      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;
  }
}
  • 处理用户空间地址减少的情况

这里我们直接仿照growproc() 的做法减少就行

/*	sysproc.c	*/
uint64
sys_sbrk(void)
{
  uint64 addr;
  int n;

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  uint64 newaddr = addr + n;
  if(newaddr >= MAXVA || newaddr <= 0)
    return addr;

  if(n < 0)
  {
    if (uvmdealloc(myproc()->pagetable, addr, newaddr) != newaddr)
      return -1;
  }
  
  myproc()->sz = newaddr;
  return addr;
}
  • 处理 fork() 中子进程复制父进程内存的情况

fork() 中我们可以看到使用 uvmcopy() 这个函数来复制父进程的页表,而在复制父进程页表的过程中自然会发现未映射的情况从而报错,我们同uvmunmap() 中修改即可

/*	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)
      continue;
    if((*pte & PTE_V) == 0)
      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;
}
  • 当调用系统调用如 read,write 的时候,内核会访问未被分配的页表,此时不会进入 usertrap, 这时也需要分配

(这个找了好久)

sys_read()->readi()->either_copyout()->copyout()->walkaddr()

uint64
walkaddr(pagetable_t pagetable, uint64 va)
{
  pte_t *pte;
  uint64 pa;

  if(va >= MAXVA)
    return 0;

  pte = walk(pagetable, va, 0);
  if(pte == 0 || (*pte & PTE_V) == 0)
  {
    struct proc *p = myproc();
    if (va >= p->sz || va < PGROUNDUP(p->trapframe->sp)) return 0;
      
    uint64 pa = (uint64)kalloc();
    if (pa == 0) return 0;
    
    if (mappages(p->pagetable, va, PGSIZE, pa, PTE_W|PTE_X|PTE_R|PTE_U) != 0)
    {
      kfree((void*)pa);
      return 0;
    }
    return pa;
  }
  if((*pte & PTE_U) == 0)
    return 0;
  pa = PTE2PA(*pte);
  return pa;
}

实验结果

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值