zoukankan      html  css  js  c++  java
  • Linux 进程等待队列【转】

    本文转载自:http://blog.csdn.net/dlutbrucezhang/article/details/9212067

    Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。

    在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为"连接件"。它通过一个双链表和把等待tast的头,和等待的进程列表链接起来。从上图可以清晰看到。所以我们知道,如果要实现一个等待队列,首先要有两个部分。队列头和队列项。下面看他们的数据结构。

    [cpp] view plain copy
     
     print?
    1. <span style="font-size:18px;"><strong>struct list_head {  
    2.     struct list_head *next, *prev;  
    3. };  
    4. struct __wait_queue_head {  
    5.     spinlock_t lock;  
    6.     struct list_head task_list;  
    7. };  
    8. typedef struct __wait_queue_head wait_queue_head_t;  
    9. struct __wait_queue {  
    10.     unsigned int flags;  
    11. #define WQ_FLAG_EXCLUSIVE    0x01  
    12.     void *private;//2.6版本是采用void指针,而以前的版本是struct task_struct * task;  
    13.                   //实际在用的时候,仍然把private赋值为task  
    14.     wait_queue_func_t func;  
    15.     struct list_head task_list;  
    16. };  
    17. </strong></span>  

     

    所以队列头和队列项是通过list_head联系到一起的,list_head是一个双向链表,在linux内核中有着广泛的应用。并且在list.h中对它有着很多的操作。

     

    2.对列头和队列项的初始化:
     wait_queue_head_t my_queue;

    init_waitqueue_head(&my_queue);

    直接定义并初始化。init_waitqueue_head()函数会将自旋锁初始化为未锁,等待队列初始化为空的双向循环链表。

    DECLARE_WAIT_QUEUE_HEAD(my_queue);

    定义并初始化

    3.定义等待队列: DECLARE_WAITQUEUE(name,tsk);

    [cpp] view plain copy
     
     print?
    1. <span style="font-size:18px;"><strong>#define   DECLARE_WAITQUEUE(name,   tsk)       /   
    2. wait_queue_t   name     =__WAITQUEUE_INITIALIZER(name,   tsk)   
    3.   
    4. #define   __WAITQUEUE_INITIALIZER(name,   tsk)   {         task:     tsk,        task_list:  {  NULL,   NULL   },  __WAITQUEUE_DEBUG_INI(name)}   
    5. </strong></span>  


    它的解释是: 
    通过DECLARE_WAITQUEUE宏将等待队列项初始化成对应的任务结构,并且用于连接的相关指针均设置为空。其中加入了调试相关代码。 
    进程通过执行下面步骤将自己加入到一个等待队列中:
    1) 调用DECLARE_WAITQUEUE()创建一个等待队列的项;
    2) 调用add_wait_queue()把自己加入到等待队列中。该队列会在进程等待的条件满足时唤醒它。在其他地方写相关代码,在事件发生时,对等的队列执行wake_up()操作。
    3) 将进程状态变更为: TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE。
    4) 如果状态被置为TASK_INTERRUPTIBLE ,则信号唤醒进程。即为伪唤醒(唤醒不是因为事件的发生),因此检查并处理信号。
    5) 检查condition是否为真,为真则没必要休眠,如果不为真,则调用scheduled()。
    6) 当进程被唤醒的时候,它会再次检查条件是否为真。真就退出循环,否则再次调用scheduled()并一直重复这步操作。
    7) condition满足后,进程将自己设置为TASK_RUNNING 并通过remove_wait_queue()退出。

     

    4.(从等待队列头中)添加/移出等待队列

    (1)add_wait_queue()函数: (2)remove_wait_queue()函数:

     

    5.等待事件:(有条件睡眠)

    1)wait_event()宏:

    [cpp] view plain copy
     
     print?
    1. <span style="font-size:18px;"><strong>#define wait_event(wq, condition) /   
    2.   
    3. do { /   
    4. if (condition) /   
    5. break; /   
    6. __wait_event(wq, condition); /   
    7. while (0)   
    8.    
    9. #define __wait_event_timeout(wq, condition, ret) /   
    10.   
    11. do { /   
    12. DEFINE_WAIT(__wait); /   
    13. /   
    14. for (;;) { /   
    15. prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); /   
    16. if (condition) /   
    17. break; /   
    18. ret = schedule_timeout(ret); /   
    19. if (!ret) /   
    20. break; /   
    21. } /   
    22. finish_wait(&wq, &__wait); /   
    23. while (0)   
    24. </strong></span>  


    在等待会列中睡眠直到condition为真。在等待的期间,进程会被置为TASK_UNINTERRUPTIBLE进入睡眠,直到condition变量变为真。每次进程被唤醒的时候都会检查condition的值.

    (2)wait_event_interruptible()函数:

    和wait_event()的区别是调用该宏在等待的过程中当前进程会被设置为TASK_INTERRUPTIBLE状态.在每次被唤醒的时候,首先检查condition是否为真,如果为真则返回,否则检查如果进程是被信号唤醒,会返回-ERESTARTSYS错误码.如果是condition为真,则返回0.

    (3)wait_event_timeout()宏:

    也与wait_event()类似.不过如果所给的睡眠时间为负数则立即返回.如果在睡眠期间被唤醒,且condition为真则返回剩余的睡眠时间,否则继续睡眠直到到达或超过给定的睡眠时间,然后返回0.

    (4)wait_event_interruptible_timeout()宏:

    与wait_event_timeout()类似,不过如果在睡眠期间被信号打断则返回ERESTARTSYS错误码.

    (5) wait_event_interruptible_exclusive()宏

    同样和wait_event_interruptible()一样,不过该睡眠的进程是一个互斥进程.

     

    6.唤醒队列:

    (1)wake_up()函数:

    唤醒等待队列.可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE状态的进程,和wait_event/wait_event_timeout成对使用.

    2)wake_up_interruptible()函数: #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

    和wake_up()唯一的区别是它只能唤醒TASK_INTERRUPTIBLE状态的进程.,与wait_event_interruptible/wait_event_interruptible_timeout/ wait_event_interruptible_exclusive成对使用.

     

    TASK_INTERRUPTIBLE,允许通过发送signal唤醒它(即可中断的睡眠状态);

    TASK_UNINTERRUPTIBLE,不接收任何 singal

     

    7.在等待队列上睡眠:(无条件睡眠,老内核使用,新内核建议不用)

     (1)sleep_on()函数:

    该函数的作用是定义一个等待队列(wait),并将当前进程添加到等待队列中(wait),然后将当前进程的状态置为TASK_UNINTERRUPTIBLE,并将等待队列(wait)添加到等待队列头(q)中。之后就被挂起直到资源可以获取,才被从等待队列头(q)中唤醒,从等待队列头中移出。在被挂起等待资源期间,该进程不能被信号唤醒。

    (2)sleep_on_timeout()函数:

     

    与sleep_on()函数的区别在于调用该函数时,如果在指定的时间内(timeout)没有获得等待的资源就会返回。实际上是调用schedule_timeout()函数实现的。值得注意的是如果所给的睡眠时间(timeout)小于0,则不会睡眠。该函数返回的是真正的睡眠时间。

    (3)interruptible_sleep_on()函数:

    该函数和sleep_on()函数唯一的区别是将当前进程的状态置为TASK_INTERRUPTINLE,这意味在睡眠如果该进程收到信号则会被唤醒。

    (4)interruptible_sleep_on_timeout()函数:

    类似于sleep_on_timeout()函数。进程在睡眠中可能在等待的时间没有到达就被信号打断而被唤醒,也可能是等待的时间到达而被唤醒。

  • 相关阅读:
    BroadcastReceiver ANR
    Interview
    转载——JavaScript学习笔记:取数组中最大值和最小值
    __proto__与prototype
    javascript中的map和reduce
    css浮动与清除浮动
    转载——自适应网页设计
    《DOM Scripting》学习笔记-——第九章 CSS-DOM
    《DOM Scripting》学习笔记-——第八章 充实文档的内容
    《DOM Scripting》学习笔记-——第七章 动态创建html内容
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7097466.html
Copyright © 2011-2022 走看看