mit6.s081 lab5.3--lazytests

MIT 6.s081 lab5.3–lazytests

No.1 写在前面的话

5.1和5.2带我们初识页表错误,并了解如何对页表错误进行处理,5.3的tests则是帮助我们增加代码的健壮性,以适应多种状况的发生。当然,因为我对中断机制理解的还是不够深刻(alarm打算重做还没写博客的原因),中间稍稍借助了答案【脏了一点】,不过对于sbrk也算是更加了解了吧。

No.2 对于健壮性的考虑

情况一:sbrk参数为负

通过lecture可知,sbrk使用户程序扩大自己的heap,其系统调用参数为整数,代表了想要申请的page数量。查看原先的growproc(),它所充当的便是调整用户heap的工作,代码如下:

// Grow or shrink user memory by n bytes.
// Return 0 on success, -1 on failure.
int
growproc(int n)
{
  uint sz;
  struct proc *p = myproc();

  sz = p->sz;//heap最底端,stack最顶端
  if(n > 0){
    if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
      return -1;
    }
  } else if(n < 0){
    sz = uvmdealloc(p->pagetable, sz, sz + n);
  }
  p->sz = sz;
  return 0;
}

不难看出,需要扩展内存利用uvmalloc,而缩减则使用uvmdealloc,后者符合要求,那么将其补充到5.2中修改的sbrk里:

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

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;
  /*
  if(growproc(n) < 0)
    return -1;
  */
  myproc()->sz += n;
  if(n < 0){
    uvmdealloc(myproc()->pagetable, addr, myproc()->sz);
  }
  return addr;
}

情况二:高于分配地址

一开始我以为高于sbrk分配的任何虚拟地址是指需要用PGROUNDDOWN判断是否高出页顶部【我的确到目前为止对于vm还是很模糊】,一头雾水的补充,然后报错,实则并不是这样。

sbrk的机制为记录想要申请的page数量,然后将p->sz增加(或减少)n个。在这里p->sz便是sbrk要申请分配的内存,如果高于此则无法进行分配;但因为要引起懒分配,必然是先记录sz引起中断再进行页分配,所以这段代码需要在trap里进行:

if(va >= p->sz){
    p->killed = 1;//出现错误终止进程
}

情况三: 父到子拷贝

根据提示,看一看fork中哪一部分是内存拷贝:

  // Copy user memory from parent to child.
  if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
  //*****
  }

追溯一下可以发现几个panic:

if((pte = walk(old, i, 0)) == 0)
    panic("uvmcopy: pte should exist");
if((*pte & PTE_V) == 0)
    panic("uvmcopy: page not present");

根据之前处理错误的情况,将panic注释并添加continue就好,不过在这里还是想补充一下为何这样做可行的原因(frans解释):

A: 我们现在是lazy allocation,我们只会为需要的内存分配物理内存page。如果我们不需要这部分内存,那么就不会存在map关系,这非常的合理。相应的,我们对于这部分内存也不能释放,因为没有实际的物理内存可以释放,所以这里最好的处理方式就是continue,跳过并处理下一个page。

Q: 之前的panic存在是有理由的,是否应该判断一下,然后对于特定的场景还是panic?

A: 为什么之前的panic会存在?对于未修改的XV6,永远也不会出现用户内存未map的情况,所以一旦出现这种情况需要panic。但是现在我们更改了XV6,所以我们需要去掉这里的panic,因为之前的不可能变成了可能。

情况五:内存不足

没有先处理情况四的原因是我还有些茫然,先处理看着简单的。

执行kalloc失败则终止进程,那么要如何判断呢?搜索kalloc()查看注释:

// Returns 0 if the memory cannot be allocated.

ok,非常简单易懂,那么在trap中对分配内存的部分进行判断:

if(mem == 0){
    p->killed = 1;
}else{
    //分配内存

情况六:用户栈下的无效页面

说实话,因为对于汇编和各种寄存器指针并不熟悉,第一眼我并没有思路,不懂何为用户栈,只得看一看之前关于栈的分布图:

Imgur

唯一与栈有关系的就是sp了,这时候我忽然想起,这不是栈指针吗?既然它在底部,如果低于它那么一定会错,这不就符合条件了吗?

兴高采烈一个p->sp才发现不对,又停了下来,究竟该如何获得sp呢?(怪我没理解trap那里Morris的演示)

再次梳理trap课程,此时我们已经在trap.c的位置对中断进行处理,而中断处理的寄存器在trampoline中,不能直接调用,所以应该这样写:

if(va < p->trapframe->sp){
    p->killed = 1;//有错就杀死!
}

至此,只剩下最后一种情况。

情况四:传递地址但未分配内存

提示中sbrk向系统调用传递地址,但我用sbrk中的growproc追溯了一遍也不知道它是怎么给write或read传递的【好废】,没办法,只得换一种思路,直接看看这两系统调用,或许有线索。

直接追溯sys_read,可知它的路线为:fileread->readi->either_copyout->copyout->walkaddr,终于走到终点才知道我们真正需要修改的是哪个函数,既然该内存地址尚未分配,那将其分配就好,可以利用我们已经写好的集大成代码块;而其又提示道有效地址,我们很容易就能知道在哪里修改:

  if(pte == 0 || (*pte & PTE_V) == 0){//有效的页表条目和有效位,合并起来一起判断

对于此前上面的各种情况进行整理,trap中的代码整理重写如下:

else if(r_scause() == 13 || r_scause() == 15){
    uint64 va = r_stval();
    
    if(va >= p->sz){
      p->killed = 1;
    }
    
    va = PGROUNDDOWN(va);
	
    if(va < p->trapframe->sp){
      p->killed = 1;
    }
    //对于vm的判断到此结束,现在为分配内存的判断
    uint64* mem = (uint64*)kalloc();

    if(mem == 0){
      p->killed = 1;
    }else{
      memset(mem, 0, PGSIZE);
      if(mappages(p->pagetable, va, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
        kfree(mem);
        p->killed = 1;
      }
    }
  }

将其全部复制过来,粘贴到walkaddr,并进行修改:

  struct proc *p = myproc();//照常添加

  if(va >= MAXVA)
    return 0;

  pte = walk(pagetable, va, 0);

  if(pte == 0 || (*pte & PTE_V) == 0){
    if(va >= p->sz) return 0;
    if(va < p->trapframe->sp) return 0;

    uint64* mem = (uint64*)kalloc();
    if(mem == 0) return 0;
    memset(mem, 0, PGSIZE);  
    //uint64 a = PGROUNDUP(va);
    //一些文章在这里利用dup,虽然用down也能通过测试,但我不知道用dup的原因是什么
    if(mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
        kfree(mem);
        return 0;
      }
  }


  if((*pte & PTE_U) == 0)
    return 0;

(寻找修改部分我参看了答案)。

【用时一天半】

  • 38
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值