zoukankan      html  css  js  c++  java
  • 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()挂起的等待队列就被唤醒回来继续执行了。

  • 相关阅读:
    士兵队列训练问题http://acm.hdu.edu.cn/showproblem.php?pid=1276
    Fence Repair http://poj.org/problem?id=3253
    A/B http://acm.hdu.edu.cn/showproblem.php?pid=1576
    房间安排http://acm.nyist.net/JudgeOnline/problem.php?pid=168
    Function Run Fun http://poj.org/problem?id=1579
    盗梦空间http://acm.nyist.net/JudgeOnline/problem.php?pid=125
    如何在Chrome中使用”并非来自Chrome网上应用店”的扩展程序
    C++中的函数指针模板
    C/C++中的源文件与头文件的区别
    C++同名函数的各种问题
  • 原文地址:https://www.cnblogs.com/yangjiguang/p/7622107.html
Copyright © 2011-2022 走看看