Linux 进程的睡眠和唤醒

💡 原文中文,约4900字,阅读约需12分钟。
📝

内容提要

Linux系统中的进程可以主动释放CPU控制权,进入可中断或不可中断的休眠状态等待特定事件发生。避免无效唤醒问题的关键是在进程检查条件之前将其状态置为休眠状态,并在条件满足时将其状态重新设置为运行状态。

🔎

延伸解读

无效唤醒的根源:检查与睡眠之间的竞争窗口

文章指出,无效唤醒源于进程在检查条件之后、真正进入睡眠之前存在一个时间窗口。如果另一个进程在此窗口内修改条件并尝试唤醒,由于当前进程状态仍为 TASK_RUNNING,唤醒操作不会生效。随后当前进程错误地进入睡眠,导致即使条件已满足也无法被唤醒。这本质上是竞争条件,需要将条件检查与状态设置合并为不可分割的步骤。

修复思路:先设状态,再检查条件

避免无效唤醒的关键是调整顺序:在检查条件之前就将进程状态设置为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE。这样,如果条件在检查前已满足,后续的唤醒操作能正确将状态改为 TASK_RUNNING,进程不会错误睡眠。文章通过修改后的代码示例展示了这一顺序调整,并强调条件满足后需恢复 TASK_RUNNING 状态。

内核标准模式:等待队列与循环检查

Linux 内核推荐使用等待队列配合循环检查来安全睡眠。典型步骤包括:声明等待队列项、加入队列、设置睡眠状态,然后在 while 循环中检查条件,不满足则调用 schedule()。条件满足后,将状态设回 TASK_RUNNING 并移出队列。这种模式确保即使条件提前满足,进程也不会错过唤醒,从而避免无效唤醒问题。

实际内核案例:migration_thread 的睡眠逻辑

文章引用 Linux 2.6 内核中 migration_thread 的代码,展示其如何避免无效唤醒。该线程先设置 TASK_INTERRUPTIBLE,再循环检查 kthread_should_stop(),不满足则调用 schedule() 并重新设置状态。由于状态设置先于条件检查,即使其他进程在检查后、调度前尝试唤醒,也不会失效。这印证了内核代码遵循先设状态、后检查条件的原则。

❓

Q&A

Linux进程的休眠状态有哪些类型?

Linux进程的休眠状态分为可中断休眠状态和不可中断休眠状态。

如何在Linux中使进程进入休眠状态?

可以通过调用schedule()函数使进程进入休眠状态。

什么是无效唤醒问题?

无效唤醒问题是指进程在条件检查后进入睡眠状态时错过了唤醒信号,导致无限期休眠。

如何避免Linux中的无效唤醒问题?

避免无效唤醒的关键是在检查条件之前将进程状态置为睡眠状态。

进程如何在满足条件后重新设置为运行状态?

可以通过调用set_current_state(TASK_RUNNING)将进程状态重新设置为运行状态。

在Linux中,如何安全地管理进程的睡眠和唤醒?

可以使用等待队列和状态设置来安全地管理进程的睡眠和唤醒。

🏷️

标签

➡️

继续阅读