聚焦内核休眠机制,核心解决进程依赖其他任务完成、等待特定条件或状态变化的问题,是设备驱动开发中处理同步的关键支撑。Linux内核主要通过完成量(Completion)与等待队列(Wait Queue)两大机制,实现进程的休眠与唤醒,保障任务有序协同。
.1 完成量:等待任务完成的高效机制
完成量专为等待独立活动完成而设计,是sleep()的理想替代,尤其适用于DMA传输等场景,需引入<linux/completion.h>头文件。其核心结构体struct completion包含计数器done与等待队列头,支持两种初始化方式:
静态调用DECLARE_COMPLETION(my_comp),动态通过init_completion(&dynamic_object->my_comp)实现。
使用时,等待进程调用wait_for_completion()进入休眠,待任务完成时,执行方调用complete()唤醒单个等待进程,或调用complete_all()唤醒所有等待进程。该机制灵活性极强,wait_for_completion()与complete()的调用顺序不影响功能,即便先唤醒再等待,等待进程也能立即响应。
需注意,wait_for_completion()默认将进程设为不可中断状态(TASK_UNINTERRUPTIBLE),对外部信号无响应,易导致长时间阻塞。为此,内核提供可中断、可被kill信号中断及带超时的变体:wait_for_completion_interruptible()允许被任意信号中断,wait_for_completion_killable()仅响应致命信号,带timeout后缀的变体则设定最大等待时长,超时单位为jiffies,可通过msecs_to_jiffies()、usecs_to_jiffies()转换毫秒、微秒为jiffies。
所有等待函数仅能在进程上下文使用,且可中断、killable变体需严格检查返回值:被信号中断返回-ERESTARTSYS,任务完成返回0;超时变体超时返回0,超时前完成则返回剩余jiffies。而complete()与complete_all()可在中断上下文安全调用,无休眠风险。
.2 等待队列:等待条件与资源的灵活工具
等待队列是处理阻塞I/O、等待条件成立、感知资源可用的核心机制,本质是由自旋锁保护的进程链表,链表中的进程处于休眠状态,待条件满足时被唤醒。
其初始化支持静态DECLARE_WAIT_QUEUE_HEAD(my_event)与动态init_waitqueue_head(&my_event)两种方式。进程休眠需调用wait_event()或wait_event_interruptible(),二者仅在条件为假时让进程进入不可中断或可中断休眠状态,并从运行队列移除;若需超时控制,可使用wait_event_timeout(),超时返回0,条件满足返回剩余jiffies。
当条件发生变化时,需调用唤醒函数触发进程唤醒,包括wake_up()、wake_up_all()、wake_up_interruptible()、wake_up_interruptible_all(),唤醒时会重新评估条件,条件为真才真正唤醒进程。
使用等待队列时,必须重视返回值检查。以wait_event_interruptible()为例,若被信号中断返回非零值,此时条件未必满足,若直接访问数据,极易引发无效指针引用,导致系统崩溃。正确做法是判断返回值,被中断时妥善处理,不贸然访问数据。多数场景建议使用可中断变体,仅关键任务才采用不可中断的wait_event()。
综上,完成量与等待队列是Linux内核处理等待与阻塞的核心工具,前者聚焦任务完成等待,后者侧重条件与资源等待,二者共同为设备驱动开发提供可靠的同步保障,使用时需严格遵循上下文限制与返回值检查规范,规避系统风险。