睡眠锁 (SleepLock)
SleepLock 是一种互斥锁,当任务尝试获取一个已被占用的锁时,它不会自旋空等,而是会将任务置于睡眠状态,并让出CPU给其他任务执行。
源码链接: os/src/sync/sleep_lock.rs
1. 工作原理
SleepLock 的实现依赖于调度器和 WaitQueue 机制。
-
内部状态:
SleepLock内部包含一个布尔值locked表示锁状态,一个RawSpinLock用于保护locked字段本身,以及一个WaitQueue用于管理等待此锁的睡眠任务。 -
获取锁 (
lock): a. 任务尝试获取锁。它首先获取内部的RawSpinLock。 b. 检查locked字段。如果锁未被占用 (false),则将locked设置为true,释放RawSpinLock,获取锁成功。 c. 如果锁已被占用 (true),任务会将自己加入到WaitQueue中,然后调用sleep_task()进入睡眠状态。在睡眠前,它会释放内部的RawSpinLock。 -
释放锁 (
unlock): a. 持有锁的任务完成操作后调用unlock()。 b. 它获取内部的RawSpinLock,将locked设置为false。 c. 调用WaitQueue的wake_up_one()方法,唤醒一个正在等待队列中睡眠的任务。 d. 释放RawSpinLock。被唤醒的任务将有机会在下一次调度时运行,并再次尝试获取锁。
2. 核心接口
pub fn new() -> Self: 创建一个新的SleepLock。注意它不直接包裹数据,通常用于保护一段代码逻辑。pub fn lock(&mut self): 获取锁。如果锁被占用,将阻塞当前任务(使其睡眠)。pub fn unlock(&mut self): 释放锁,并唤醒一个等待者。
3. 适用场景
优点:
- 当锁的争用激烈或临界区执行时间较长时,它不会像自旋锁那样浪费CPU资源,而是通过任务调度提高了系统整体的吞吐量。
缺点:
- 涉及任务的睡眠和唤醒,有上下文切换的开销,因此比
SpinLock更“重”。 SleepLock只能在任务上下文中使用,绝对不能在中断处理程序中使用,因为中断处理程序没有任务上下文,无法被调度或睡眠。
结论: SleepLock 适用于保护那些访问时间较长或可能发生阻塞的临界区。例如,文件系统操作、复杂的设备I/O等。