Linux等待队列与唤醒

Posted 天地有大美而不言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux等待队列与唤醒相关的知识,希望对你有一定的参考价值。

1.数据结构

1.1等待队列头

1 struct __wait_queue_head {
2     spinlock_t lock;
3     struct list_head task_list;
4 };
5 typedef struct __wait_queue_head wait_queue_head_t;

初始化等待队列头

1 #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                2     .lock        = __SPIN_LOCK_UNLOCKED(name.lock),        3     .task_list    = { &(name).task_list, &(name).task_list } }
4 
5 #define DECLARE_WAIT_QUEUE_HEAD(name) 6     wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

 

1.2等待队列

 1 typedef struct __wait_queue wait_queue_t;
 2 typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key);
 3 int default_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key);
 4 
 5 struct __wait_queue {
 6     unsigned int flags;
 7 #define WQ_FLAG_EXCLUSIVE    0x01
 8     void *private;
 9     wait_queue_func_t func;
10     struct list_head task_list;
11 };

初始化等待队列

1 #define __WAITQUEUE_INITIALIZER(name, tsk) {                2     .private    = tsk,                        3     .func        = default_wake_function,            4     .task_list    = { NULL, NULL } }
5 
6 #define DECLARE_WAITQUEUE(name, tsk)                    7     wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

 

等待队列的task_list加入等待队列头的task_list链表。一般将wait_queue_func_t赋值为下面的默认处理函数:

1 int default_wake_function(wait_queue_t *curr, unsigned mode, int sync,
2               void *key)
3 {
4     return try_to_wake_up(curr->private, mode, sync);
5 }

1.3添加/删除等待队列

 1 static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
 2 {
 3     list_add(&new->task_list, &head->task_list);
 4 }
 5 
 6 static inline void __remove_wait_queue(wait_queue_head_t *head,
 7                             wait_queue_t *old)
 8 {
 9     list_del(&old->task_list);
10 }

2等待事件

调用以下四个宏等待事件,等待以第一个参数作为等待队列头的等待队列被唤醒,第二个参数condition必须被满足,否则继续阻塞。

wait_event()和wait_event_interruptible()的区别是后者可以被信号打断,而前者不能。

加上timeout后的宏意味着阻塞等待的超时时间,以jiffy为单位,在timeout到达时,不论condition是否满足,均返回。

1 #define wait_event(wq, condition) 
2 #define wait_event_timeout(wq, condition, timeout)    
3 #define wait_event_interruptible(wq, condition)    
4 #define wait_event_interruptible_timeout(wq, condition, timeout)

下面以wait_event()为例分析执行过程

技术分享
 1 #define wait_event(wq, condition)                      2 do {                                     3     if (condition)                              4         break;                             5     __wait_event(wq, condition);                     6 } while (0)
 7 #define __wait_event(wq, condition)                      8 do {                                     9     DEFINE_WAIT(__wait);                        10                                     11     for (;;) {                            12         prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);    13         if (condition)                        14             break;                        15         schedule();                        16     }                                17     finish_wait(&wq, &__wait);                    18 } while (0)
wait_event(wq, condition)
 wait_event(wq, condition) 
     -->__wait_event(wq, condition);
         -->DEFINE_WAIT(__wait);    
         -->prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);
             -->__add_wait_queue(q, wait);//将等待队列添加到等待队列头
             -->set_current_state(state);
         -->schedule();    //切换到其它进程
         -->被唤醒切换回来
         -->finish_wait(&wq, &__wait);    
             -->__set_current_state(TASK_RUNNING);
             -->list_del_init(&wait->task_list);//将唤醒的等待队列删除

注意上面的DEFINE_WAIT(__wait)宏展开如下

1 #define DEFINE_WAIT(name)                        2     wait_queue_t name = {                        3         .private    = current,                4         .func        = autoremove_wake_function,        5         .task_list    = LIST_HEAD_INIT((name).task_list),    6     }
技术分享
1 int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
2 {
3     int ret = default_wake_function(wait, mode, sync, key);
4 
5     if (ret)
6         list_del_init(&wait->task_list);
7     return ret;
8 }
autoremove_wake_function

3.唤醒队列

wake_up()可唤醒处于TASK_UNINTERRUPTIBLE 和 TASK_INTERRUPTIBLE的进程,而wake_up_interruptible只能唤醒处于TASK_INTERRUPTIBLE的进程。

#define wake_up(x)            __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

 下面以wake_up()为例分析,

1 wake_up(x)
2     -->__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
3         -->__wake_up_common(q, mode, nr_exclusive, 0, key);//唤醒等待队列头链表中的所有等待队列

关键在于__wake_up_common()函数

 1 /*
 2  * The core wakeup function.  Non-exclusive wakeups (nr_exclusive == 0) just
 3  * wake everything up.  If it‘s an exclusive wakeup (nr_exclusive == small +ve
 4  * number) then we wake all the non-exclusive tasks and one exclusive task.
 5  *
 6  * There are circumstances in which we can try to wake a task which has already
 7  * started to run but is not in state TASK_RUNNING.  try_to_wake_up() returns
 8  * zero in this (rare) case, and we handle it by continuing to scan the queue.
 9  */
10 static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
11                  int nr_exclusive, int sync, void *key)
12 {
13     struct list_head *tmp, *next;
14 
15     list_for_each_safe(tmp, next, &q->task_list) {
16         wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
17         unsigned flags = curr->flags;
18 
19         if (curr->func(curr, mode, sync, key) &&
20                 (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
21             break;
22     }
23 }

其中的curr->func即指向autoremove_wake_function()函数。

唤醒进程是在try_to_wake_up(curr->private, mode, sync)函数中具体执行的,唤醒较复杂,后面有时间在分析。

可以简单理解为执行完该函数,2中wait_event()挂起的等待队列就被唤醒回来继续执行了。

以上是关于Linux等待队列与唤醒的主要内容,如果未能解决你的问题,请参考以下文章

在Linux驱动程序中,使用等待队列的作用?

Java多线程:阻塞队列与等待唤醒机制初探

Linux队列

linux中的等待队列-51

linux中的等待队列-51

linux中的等待队列-51