HW7:xv6 locking

DeadLock

  struct spinlock lk;
  initlock(&lk, "test lock");
  acquire(&lk);
  acquire(&lk);

要求我们用一句话说明,上述的代码出现的问题。
答:由于在第一次acquire之后,并未release就进行了acquire操作,导致第二次的acuqire会永远获取不到锁,即死锁。

Interrupt in ide.c

在xv6的代码中,acquire函数会通过cli指令,屏蔽中断;release函数通过sti指令,将中断打开。但是,如果在ide.ciderw函数中,我们在acquire函数的后面加上sti指令,而在release函数的前面加上cli指令,并重新编译内核,那么xv6就有可能boot失败,请找出原因。
答:

  1. panic函数
    如下面的代码显示,getcallerpcs函数会将栈中存储的eip信息存放到pcs数组中,然后打印pcs数组。
void
panic(char *s)
{
  int i;
  uint pcs[10];
  cli();
  cons.locking = 0;
  // use lapiccpunum so that we can call panic from mycpu()
  cprintf("lapicid %d: panic: ", lapicid());
  cprintf(s);
  cprintf("\n");
  getcallerpcs(&s, pcs);
  for(i=0; i<10; i++)
    cprintf(" %p", pcs[i]);
  panicked = 1; // freeze other CPU
  for(;;)
    ;
}


// Record the current call stack in pcs[] by following the %ebp chain.
void
getcallerpcs(void *v, uint pcs[])
{
  uint *ebp;
  int i;
  ebp = (uint*)v - 2;
  for(i = 0; i < 10; i++){
    if(ebp == 0 || ebp < (uint*)KERNBASE || ebp == (uint*)0xffffffff)
      break;
    pcs[i] = ebp[1];     // saved %eip
    ebp = (uint*)ebp[0]; // saved %ebp
  }
  for(; i < 10; i++)
    pcs[i] = 0;
}
  1. 从下图中可以看出,xv6在sched locks的地方被panic
    在这里插入图片描述
  • kernel.asm中,依次查找eip中的值,就可以查出调用栈了,调用栈为:
    trapasm.S: trapret -> proc.c: forkret -> fs.c: iinit -> fs.c: readsb ->bio.c: bread-> ide.c: iderw -> trapasm.S: alltraps -> trap.c: trap -> proc.c: yield -> proc.c: sched
  • 从调用栈中可以看出,在ide.c:iderw函数中,调用到了acquire(&idelock)之后,由于sti指令被加入,所以中断又重新可以被触发,因此触发了进程切换trapams.S: alltraps,此时需要当前进程放弃CPU,调用到了yield函数,判断
    if(mycpu()->ncli != 1)的时候出错。

interrupts in file.c

将上述的sti()cli()去除,重新编译kernel,并保证kernel能够重新正常运行。
现在和上述的实验一样,我们在获得file_table_lock的时候,通过sti,不再屏蔽中断。file_table_lock用来保护文件描述符表,当我们在打开和关闭文件的时候,会修改这个文件描述符表。在file.c文件中的filealloc()中,在acquire()函数被调用后面加入sti(),然后在每一处调用到release()的前面,加入cli()
重新编译kernel,然后在qemu中将它启动,kernel并不会panic
解释一下,为什么我们对file_table_lockide_lock做同样的操作,但是却出现了不同的表现。
kernelfile_table_lockide_lock的使用情况来看,在xv6中没有中断处理函数会争 ftable.lock 保护的资源。自然也就不会 acquire(&ftable.lock)

xv6 lock implementation

Why does release() clear lk->pcs[0] and lk->cpu before clearing lk->locked? Why not wait until after?
答:因为在acquire的时候,会调用getcallerpcs来记录debug信息,即lk->pcs[0]和lk->cpu。因此当线程1在release的时候,如果lk->locked先被释放,会导致线程2通过getcallerpcs来修改lk->pcs[0]和lk->cpu,同时线程1自己也在修改lk->pcs[0]和lk->cpu,形成竞争条件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值