wait_event_interruptible 等到条件并被wakeup执行时,再次执行 wake_up_interruptible 会发生什么

本文详细解析了Linux内核中的wait_event_interruptible函数的工作原理,包括prepare_to_wait和finish_wait函数的作用,以及wake_up_interruptible如何唤醒等待队列中的线程。探讨了TASK_INTERRUPTIBLE状态下的线程行为,以及在特定条件下唤醒操作的有效性和无效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

wait_event_interruptible souece code

#define __wait_event_interruptible(wq, condition, ret)			\
do {									\
	DEFINE_WAIT(__wait);						\
									\
	for (;;) {							\
		prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);	\
		if (condition)						\
			break;						\
		if (!signal_pending(current)) {				\
			schedule();					\
			continue;					\
		}							\
		ret = -ERESTARTSYS;					\
		break;							\
	}								\
	finish_wait(&wq, &__wait);					\
} while (0)

当 wait_event_interruptible 条件成立且被执行时,就到了finish_wait。

/**
 * finish_wait - clean up after waiting in a queue
 * @q: waitqueue waited on
 * @wait: wait descriptor
 *
 * Sets current thread back to running state and removes
 * the wait descriptor from the given waitqueue if still
 * queued.
 */
void finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
	unsigned long flags;

	__set_current_state(TASK_RUNNING);
	/*
	 * We can check for list emptiness outside the lock
	 * IFF:
	 *  - we use the "careful" check that verifies both
	 *    the next and prev pointers, so that there cannot
	 *    be any half-pending updates in progress on other
	 *    CPU's that we haven't seen yet (and that might
	 *    still change the stack area.
	 * and
	 *  - all other users take the lock (ie we can only
	 *    have _one_ other CPU that looks at or modifies
	 *    the list).
	 */
	if (!list_empty_careful(&wait->task_list)) {
		spin_lock_irqsave(&q->lock, flags);
		list_del_init(&wait->task_list);
		spin_unlock_irqrestore(&q->lock, flags);
	}
}

我们先看看 prepare_to_wait 做了什么

*
 * Note: we use "set_current_state()" _after_ the wait-queue add,
 * because we need a memory barrier there on SMP, so that any
 * wake-function that tests for the wait-queue being active
 * will be guaranteed to see waitqueue addition _or_ subsequent
 * tests in this thread will see the wakeup having taken place.
 *
 * The spin_unlock() itself is semi-permeable and only protects
 * one way (it only protects stuff inside the critical region and
 * stops them from bleeding out - it would still allow subsequent
 * loads to move into the critical region).
 */
void
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
	unsigned long flags;

	wait->flags &= ~WQ_FLAG_EXCLUSIVE;
	spin_lock_irqsave(&q->lock, flags);
	if (list_empty(&wait->task_list))
		__add_wait_queue(q, wait);
	set_current_state(state);
	spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(prepare_to_wait);

prepare_to_wait 做了 __add_wait_queue(q, wait);而
finish_wait 做了 list_del_init(&wait->task_list);
list_del_init() 函数用于将一个节点从链表中移除,并初始化这个节点。函数将节点 传入 __list_del_entry() 函数,移除将节点从链表中移除,然后调用 INIT_LIST_HEAD() 函数初始化这个节点

这个时候我们再看看 wake_up_interruptible 的原理

/*
 * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just
 * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve
 * number) then we wake all the non-exclusive tasks and one exclusive task.
 *
 * There are circumstances in which we can try to wake a task which has already
 * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns
 * zero in this (rare) case, and we handle it by continuing to scan the queue.
 */
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
			int nr_exclusive, int wake_flags, void *key)
{
	wait_queue_t *curr, *next;

	list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
		unsigned flags = curr->flags;

		if (curr->func(curr, mode, wake_flags, key) &&
				(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
			break;
	}
}

首先翻译一下:
在某些情况下,我们可以尝试唤醒已经开始运行但未处于TASK_RUNNING状态的任务。 在这种(罕见)情况下,try_to_wake_up()返回零,我们通过继续扫描队列来处理它。

  1. 唤醒已经开始运行但未处于TASK_RUNNING状态
  2. 唤醒已经开始运行但处于TASK_RUNNING状态

即 q->task_list 为空时,且处于TASK_RUNNING,就不会去执行 try_to_wake_up,遇到罕见的情形,理论上应该也是在唤醒途中,发现任务已经被执行就停止了唤醒操作。

threadA
for(;;)
{
	wait_event_interruptible 
	do something
}

threadB

wake_up_interruptible

伪代码的解释如下:

  1. threadA 在 等待被唤醒
  2. threadB 唤醒了 threadA
  3. threadA 清空了整个task_list
  4. thread A 在做do something
  5. threadB 在 threadA 重新循环进入wait_event_interruptible前执行wake_up_interruptible 都是无效的操作

总结:
wait_event_interruptible 等到条件并被wakeup执行时,再次执行 wake_up_interruptible 时并不会有什么唤醒的动作,因为q->task_list链表上空的,即此次的 wake_up_interruptible 是无效的。
PS :

  1. TASK_INTERRUPTIBLE 可中断的睡眠状态。是指被信号中断:signal
  2. prepare_to_wait -> schedule 后就进入了睡眠,被唤醒的前提wake_up 和 condition为true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值