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,就退出循环,在这之前,不管遇到多少非排他性任务,都会从队列取出,并唤醒。