Sleep函数详解
xv6 的 sleep 函数设计极其精妙,它要求调用者必须持有保护条件的锁 (lk)。
函数原型
void sleep(void *chan, struct spinlock *lk);
chan: 睡眠通道(通常是等待数据的地址)。lk: 保护“等待条件”的锁(如pipe->lock)。
魔法流程
- 持有进程锁:
acquire(&p->lock)。必须持有自己的锁才能修改状态 (p->state)。 - 释放条件锁:
release(lk)。- 关键: 带着
lk睡觉会导致死锁(别人没法写数据了)。 - 原子性: 此时虽然放了
lk,但我们拿到了p->lock。如果此时有人想wakeup,他必须获取p->lock,所以他会被卡住,直到我们真正睡着。
- 关键: 带着
- 修改状态:
p->state = SLEEPING; p->chan = chan; - 切换:
sched()。切到调度器。 - 苏醒: 当被唤醒并重新获得 CPU 后。
- 收尾:
release(&p->lock)。 - 重新拿锁:
acquire(lk)。恢复调用前的状态。
总结: sleep 实现了 “释放锁 + 进入睡眠” 的原子性,利用了 p->lock 作为中间的接力棒。