linux 等待队列

原创 2012年03月22日 10:02:27

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

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

等待队列在源代码树include/linux/wait.h中,这是一个通过list_head连接的典型双循环链表,如下图所示。

   1. 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. };  

所以队列头和队列项是通过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);

   1. #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)}   

它的解释是:
通过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()退出。


static inline void init_waitqueue_head(wait_queue_head_t *q)
该函数初始化一个已经存在的等待队列头,它将整个队列设置为"未上锁"状态,并将链表指针prev和next指向它自身。
{
    q->lock = WAITQUEUE_RW_LOCK_UNLOCKED;
    INIT_LIST_HEAD(&q->task_list);
}

 

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
该函数初始化一个已经存在的等待队列项,它设置对应的任务结构(进程),同时将标志位清0
{
    q->flags = 0;
    q->task = p;
}

 

static inline int waitqueue_active(wait_queue_head_t *q)
该函数检查等待队列是否为空。
{
    return  
!list_empty(&q->task_list);
}

 

static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
将指定的等待队列项new添加到等待队列头head所在的链表头部,该函数假设已经获得锁。
{
    list_add(&new->task_list, &head->task_list);
}

 

static inline void __add_wait_queue_tail(wait_queue_head_t *head, wait_queue_t *new)

将指定的等待队列项new添加到等待队列头head所在的链表尾部,该函数假设已经获得锁。

{

    list_add_tail(&new->task_list, &head->task_list);

}

 

static inline void  __remove_wait_queue(wait_queue_head_t *head, wait_queue_t *old)

将函数从等待队列头head所在的链表中删除指定等待队列项old,该函数假设已经获得锁,并且old在head所在链表中。

{

    list_del(&old->task_list);

}

 

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

1)wait_event()宏:


   1. #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)   

在等待会列中睡眠直到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_INTERRUPTIBLETASK_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()函数。进程在睡眠中可能在等待的时间没有到达就被信号打断而被唤醒,也可能是等待的时间到达而被唤醒。

Linux 进程控制——等待队列详解

一、什么是睡眠     对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状态并且从调度器的运行队列中去除. 直到发生某些事情改变了那个状态, 这个进程将不被在任何 ...
  • lizuobin2
  • lizuobin2
  • 2016年06月29日 21:48
  • 3548

Linux kernel 中的的等待队列(阻塞式)

等待队列在Linux内核中用来阻塞或唤醒一个进程,也可以用来同步对系统资源的访问,还可以实现延迟功能 等待队列接口函数介绍: #include //头文件包含 1....
  • xyfabcde
  • xyfabcde
  • 2017年06月01日 12:25
  • 503

Linux 工作队列和等待队列的区别

work queue是一种bottom half,中断处理的后半程,强调的是动态的概念,即work是重点,而queue是其次。 wait queue是一种「任务队列」,可以把一些进程放在上面睡眠...
  • wujiangguizhen
  • wujiangguizhen
  • 2013年12月05日 15:04
  • 1518

Linux设备驱动六 (1)等待队列

本文转自 reille博客: http://velep.com/archives/815.html 等待队列在Linux内核中有着举足轻重的作用,很多linux驱动都或多或少涉及到了等待队列。因此...
  • dagefeijiqumeiguo
  • dagefeijiqumeiguo
  • 2016年12月30日 12:09
  • 376

Linux内核中等待队列 和完成量

inux内核中等待队列 (函数wait_event与wake_up) 分类: linux内核技术2012-01-05 00:03 7905人阅读 评论(6) 收藏 举报 wait_...
  • pi9nc
  • pi9nc
  • 2014年05月23日 10:30
  • 3416

linux之等待队列

动态定义 DECLARE_WAITQUEUE(wait, current); 展开看宏定义,  kernel/include/linux/wait.h  #define __WAITQUEUE...
  • chenpuo
  • chenpuo
  • 2017年01月10日 18:01
  • 604

Linux 驱动之中断下半部之工作队列

1、工作队列的使用 按惯例,在介绍工作队列如何实现之前,先说说如何使用工作队列实现下半部。 步骤一、定义并初始化工作队列: 创建工作队列函数: struct workqueue_struct *...
  • xy010902100449
  • xy010902100449
  • 2015年06月03日 15:45
  • 1732

linux poll 和 等待队列休眠的关系(基于kernel 3.10.0)

原理分析来自网上,代码由本人梳理分析阅读vhost的时候,发现使用了大量的等待队列和poll,这里温故而知新一下。注:wait_queue_t是等待在wait_queue_head_t队列中的等待元素...
  • leoufung
  • leoufung
  • 2015年09月28日 09:56
  • 548

linux中等待队列的应用

Linux内核里的等待队列机制在做驱动开发时用的非常多,多用来实现阻塞式访问,下面简单总结了等待队列的四种用法,希望对读者有所帮助。 1. 睡眠等待某个条件发生(条件为假时睡眠):     睡眠方...
  • yanxuex
  • yanxuex
  • 2013年12月31日 16:38
  • 468

linux等待队列

等待队列头的结构体声明如下: 定义并初始化一个等待队列头 #define DECLARE_WAIT_QUEUE_HEAD(name) \ wait_queue_head_t name...
  • zwmyxfbenet
  • zwmyxfbenet
  • 2014年06月26日 16:16
  • 297
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux 等待队列
举报原因:
原因补充:

(最多只允许输入30个字)