Sleep函数详解

xv6 的 sleep 函数设计极其精妙,它要求调用者必须持有保护条件的锁 (lk)。

函数原型

void sleep(void *chan, struct spinlock *lk);

  • chan: 睡眠通道(通常是等待数据的地址)。
  • lk: 保护“等待条件”的锁(如 pipe->lock)。

魔法流程

  1. 持有进程锁: acquire(&p->lock)。必须持有自己的锁才能修改状态 (p->state)。
  2. 释放条件锁: release(lk)
    • 关键: 带着 lk 睡觉会导致死锁(别人没法写数据了)。
    • 原子性: 此时虽然放了 lk,但我们拿到了 p->lock。如果此时有人想 wakeup,他必须获取 p->lock,所以他会被卡住,直到我们真正睡着。
  3. 修改状态: p->state = SLEEPING; p->chan = chan;
  4. 切换: sched()。切到调度器。
  5. 苏醒: 当被唤醒并重新获得 CPU 后。
  6. 收尾: release(&p->lock)
  7. 重新拿锁: acquire(lk)。恢复调用前的状态。

总结: sleep 实现了 “释放锁 + 进入睡眠” 的原子性,利用了 p->lock 作为中间的接力棒。