wait_event()函数和wake_up()函数一些理解

1.wait_event()函数 

wait_event()会调用__wait_event。

    #define __wait_event(wq, condition) 					\
do {	                                                    \
    /*定义一个wait_queue_t类型的变量__wait并初始化,其中func指向autoremove_wake_function(),
      该函数在wake_up()时候被调用,用于唤醒线程*/								\
	DEFINE_WAIT(__wait);						\
	for (;;) {                                                                \
        /*将上面定义的__wait,添加到wq里面的链表中*/							
		prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);	\
		if (condition)						\
			break;  \
        /*如果条件不满足,则让出CPU,直到其他任务调用wake_up函数将其唤醒*/						
		schedule();						\
	}								\
    /*将__wait,从wq中删除*/
	finish_wait(&wq, &__wait);					\
} while (0)

其中prepare_to_wait代码如下,里面有个"WQ_FLAG_EXCLUSIVE",比较重要,它表示该任务是否是“排他性”的唤醒的,其实就是我被唤醒之后,是否允许其他任务也被唤醒,在wake_up时候会用到。

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);
}

 

2.wake_up()函数

wake_up有很多相关的变体函数,如下:

#define wake_up(x)			__wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_nr(x, nr)		__wake_up(x, TASK_NORMAL, nr, NULL)
#define wake_up_all(x)			__wake_up(x, TASK_NORMAL, 0, NULL)
#define wake_up_locked(x)		__wake_up_locked((x), TASK_NORMAL, 1)
#define wake_up_all_locked(x)		__wake_up_locked((x), TASK_NORMAL, 0)

其中第3个参数,内核写的是" how many wake-one or wake-many threads to wake up",我理解是允许"排他性"任务唤醒的个数,继续看内核代码。

void __wake_up(wait_queue_head_t *q, unsigned int mode,
			int nr_exclusive, void *key)
{
	unsigned long flags;

	spin_lock_irqsave(&q->lock, flags);
	__wake_up_common(q, mode, nr_exclusive, 0, key);
	spin_unlock_irqrestore(&q->lock, flags);
}

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;
    /*从链表中,依次取出等待的任务,也就是执行wait_event,加入链表的任务*/
	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;
	}
}

其中,下面这个if语句很有意思,分析一下:

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

  • curr->func(curr, mode, wake_flags, key)

执行wait_queue_t里面的func函数,也就是autoremove_wake_function(),调用关系为--->default_wake_function()--->try_to_wake_up()。try_to_wake_up()会唤醒从刚刚从链表里面取出的等待线程。

  • (flags & WQ_FLAG_EXCLUSIVE)

如果该线程不是"排他性"线程,那么第三个条件不会执行,继续从链表取出元素,并唤醒。如果是"排他性"行为,则第3个条件"!--nr_exclusive"会执行。如果这个nr_exclusive为3,那么break不会执行,继续从链表取数据,如果已经取了2个"排他性"的任务,此时nr_exclusive为1,那么第三个条件生效,list_for_each_entry_safe()函数退出。

  • !--nr_exclusive

如果第2个条件满足,该语句才会执行。如果nr_exclusive为1,那么break执行,退出循环,也即不在唤醒队列上的任务了,即使队列还有很多任务没有被唤醒。

总结wake_up是如何唤醒链表中的任务的:

  • 如果链表中所有任务都是非"排他性"任务,那么上述条件2永远不能满足,就是遍历到队列的所有任务,并唤醒,从链表删除;
  • 如果链表里面有排他性任务,并且nr_exclusive大于1,那边链表会一直取元素,直到遇到最后一个"排他性"任务,比且nr_exclusive=1,就退出循环,在这之前,不管遇到多少非排他性任务,都会从队列取出,并唤醒。
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值