6.S081 lab5: lazy

主要记录下我在写这个lab的时候遇到的问题,具体的代码网上已经有很多了 。

BUG1

理论上来讲,vm.c文件中freewalk的panic是不需要注释掉的,当时写的时候老是报错

panic freewalk: leaf

后来发现问题出在trap函数中:

else if((r_scause() == 13 || r_scause() == 15) && r_stval() >= p->heap_start){
	
	uint64 vmaddr = r_stval(); 
	if(vmaddr < p->sz){  // disaligned ? 
	  uint64 rel = uvmalloc(p->pagetable, PGROUNDDOWN(vmaddr), vmaddr + PGSIZE); // bug!
	  if(rel == 0){
		printf("no more memory\n");
		p->killed = 1;
	  }
	}

在传uvmalloc第三个参数的时候,忘了加上PGROUNDDOWN宏。导致本来是分出一页的,却分出了两页,第二页可能超出p->sz,因此uvmunmap没有把第二页对应的页表项清除,因此在freewalk中报错了。

BUG2

这个bug我应该把锅扔给xv6的开发者(或者人家是有意来坑一下?),首先看一下原来sbrk的样子:

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

  if(argint(0, &n) < 0)
    return -1;
  addr = myproc()->sz;  // 64位无符号数截断为32位有符号数!
  if(growproc(n) < 0)
    return -1;
  return addr;  //32位有符号数拓展回64位无符号数!
}

因此原本的sbrk是有隐患的,但是由于用户的内存不可能到4G,因此这个隐患没有暴露出来。但是在这个lab里面,因为延迟分配,所以用户内存是可能到达4G以上的,这里就出问题。因此修改sbrk的第一要义是,把addr的类型改为uint64

许多人给出的解法基本都是这样的

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

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

典型的缺页处理是这样:

else if(cause == 13 || cause == 15) { // 页表映射异常
    uint64 va = r_stval();
    // if(lazy_uvmalloc(p->pagetable, va) != 0) {
    //   p->killed = 1;
    // }
    if (va >= p->sz || va < p->trapframe->sp){ 
	  printf("va : %p, \np->sz: %p", va, p->sz);
      p->killed = 1;  // 如果访问地址不在用户空间内或者栈以上则杀死进程
	}
    else {  // 延迟分配
      if(lazy_uvmalloc(p->pagetable, va) != 0) {
          p->killed = 1;  //分配失败也杀死进程
      }
    }
  }

当然,这样写最后会通过测试,我们来分析一下这样写为什么能过test。
查看test3的测试逻辑,可知测试进程会不断使用malloc分配内存并消耗内存,如果malloc返回0时该进程还没有被操作系统杀死,那么测试失败。如果该进程因为耗尽内存而被系统杀死(异常中止),则测试成功

上面的代码在p->sz第一次大于 2 31 2^{31} 231时,调用sbrk,此时赋值给addr,addr符号位为1,在返回addr时,它会先进行符号拓展。因此sbrk返回的地址是一个无比巨大的uint64

然后用户进程对返回地址进行赋值,触发页错误,进入内核,这个时候va就是一个无比巨大的uint64,而且远大于p->sz,因此上面的trap逻辑会判断用户进程越界,从而杀死进程。因此test3成功通过了。但这里用户进程被杀死并不是因为耗尽内存!

最后测试验证一下:
在这里插入图片描述
上边是一个计数器,计数test3中调用了多少次malloc,可以看到,在调用了128次malloc后,返回的va编变成了一个巨大的uint64,因此被内核杀死了(参考上面写的trap中的printf语句)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值