插件窝 干货文章 Linux 进程的睡眠和唤醒

Linux 进程的睡眠和唤醒

唤醒 睡眠 进程 wait 213    来源:    2025-05-13

Linux 进程的睡眠和唤醒机制

基本概念

在Linux内核中,进程的睡眠(sleep)和唤醒(wakeup)是进程调度的重要机制,用于管理进程对资源的等待和同步。

睡眠状态

  • TASK_INTERRUPTIBLE:可中断睡眠状态,可以被信号唤醒
  • TASK_UNINTERRUPTIBLE:不可中断睡眠状态,只能由特定条件唤醒
  • TASK_KILLABLE:可终止睡眠状态(类似TASK_UNINTERRUPTIBLE但可被致命信号唤醒)

睡眠机制

基本睡眠函数

// 可中断睡眠
void interruptible_sleep_on(wait_queue_head_t *q);

// 不可中断睡眠
void sleep_on(wait_queue_head_t *q);

现代推荐用法

DEFINE_WAIT(wait);  // 定义等待队列项

prepare_to_wait(&wq_head, &wait, TASK_INTERRUPTIBLE);  // 准备睡眠
if (!condition)
    schedule();  // 让出CPU
finish_wait(&wq_head, &wait);  // 完成等待

更高级的API

wait_event(wq_head, condition);  // 不可中断等待条件
wait_event_interruptible(wq_head, condition);  // 可中断等待条件
wait_event_timeout(wq_head, condition, timeout);  // 带超时的等待

唤醒机制

基本唤醒函数

void wake_up(wait_queue_head_t *q);  // 唤醒所有等待的进程
void wake_up_interruptible(wait_queue_head_t *q);  // 只唤醒可中断睡眠的进程

唤醒单个进程

void wake_up_process(struct task_struct *tsk);

实现原理

  1. 等待队列:内核使用等待队列(wait queue)来管理睡眠的进程
  2. 调度器交互:当进程睡眠时,会从运行队列中移除
  3. 唤醒机制:当条件满足时,唤醒函数将进程重新加入运行队列

实际应用示例

// 生产者-消费者模型中的睡眠/唤醒示例
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int condition = 0;

// 消费者线程
static int consumer_thread(void *data)
{
    while (1) {
        wait_event_interruptible(wq, condition != 0);
        // 处理数据
        condition = 0;
    }
    return 0;
}

// 生产者线程
static int producer_thread(void *data)
{
    while (1) {
        // 生产数据
        condition = 1;
        wake_up_interruptible(&wq);  // 唤醒消费者
    }
    return 0;
}

注意事项

  1. 竞态条件:检查和睡眠必须是原子操作,通常使用锁保护
  2. 虚假唤醒:进程可能在没有条件满足时被唤醒,需要循环检查条件
  3. 性能考虑:频繁的睡眠/唤醒会带来上下文切换开销
  4. 死锁风险:确保唤醒不会被遗漏

调试技巧

  1. 使用ps aux查看进程状态:

    • S:可中断睡眠
    • D:不可中断睡眠
  2. 使用/proc/<pid>/wchan查看进程在等待什么内核函数

  3. 使用strace跟踪系统调用,查看进程阻塞的位置

理解Linux进程的睡眠和唤醒机制对于开发内核模块、驱动程序和高效的用户空间程序都至关重要。