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); -->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 }
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等待队列与唤醒的主要内容,如果未能解决你的问题,请参考以下文章