Linux内核中prepare_to_wait_event后抢占会导致wait_event_interruptible永久睡眠吗
wait_event_interruptible抢占场景安全性疑问解答
你的推论不成立,遗漏了Linux等待队列的核心使用约定和prepare_to_wait_event的实现保障,具体分析如下:
首先我们再明确核心宏的代码逻辑:
#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd) \ ({ \ __label__ __out; \ struct wait_queue_entry __wq_entry; \ long __ret = ret; /* explicit shadow */ \ \ init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); \ for (;;) { \ long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\ \ if (condition) \ break; \ \ if (___wait_is_interruptible(state) && __int) { \ __ret = __int; \ goto __out; \ } \ \ cmd; \ } \ finish_wait(&wq_head, &__wq_entry); \ __out: __ret; \ })
等待队列的强制使用约定
Linux内核对等待队列的使用有统一约定,所有唤醒侧的代码都必须遵守:先修改等待条件condition为真,再调用wake_up系列接口触发唤醒,这个约定是避免竞态的核心前提。
你假设的场景分析
你假设的场景不会导致进程永久挂住,逐点说明:
- 当执行到第8行(刚进入循环还没调用
prepare_to_wait_event)时另一CPU触发唤醒:
此时当前进程还没有把自己的等待队列项加入wq_head,这次唤醒无法遍历到当前进程,属于无效唤醒。但根据使用约定,唤醒侧发唤醒前已经把condition修改为真,接下来第11行判断condition会直接命中break,不会进入后续睡眠逻辑。 - 当执行到第10行(刚调用完
prepare_to_wait_event)触发中断被切出:
此时进程已经被加入等待队列,状态被设置为TASK_INTERRUPTIBLE,就算被切出也不会有问题:- 如果唤醒侧已经修改了
condition为真,进程调度回来后第11行判断条件直接break,执行finish_wait把自己移出队列,正常退出。 - 如果此时刚好有唤醒信号发到该等待队列,唤醒逻辑会直接把当前进程的状态重置为
TASK_RUNNING,等进程调度回来要么判断条件为真退出,要么走到cmd(wait_event_interruptible场景下就是schedule())的时候,发现自身是RUNNING状态,不会实际被挂起,直接返回进入下一次循环判断条件。
- 如果唤醒侧已经修改了
不需要抢占禁用的原因
prepare_to_wait_event内部已经用自旋锁处理了等待队列修改和并发唤醒的竞态,哪怕加入队列的过程中出现并发唤醒,也不会漏掉进程状态的更新,不需要外层额外禁用抢占。
内容的提问来源于stack exchange,提问作者Changda Li
相关产品推荐
相关产品推荐

