在Linux内核中等待队列有很多用途,可用于中断处理、进程同步及定时。我们在这里只说,进程经常必须等待某些事件的发生。等待队列实现了在事件上的条件等待: 希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制全。因此,等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。

等待队列由循环链表实现,其元素包括指向进程描述符的指针。每个等待队列都有一个等待队列头(wait queue head),等待队列头是一个类型为wait_queue_head_t的数据结构

(1)定义等待队列头(相关内容可以在linux/include/wait.h中找到)

等待队列头结构体的定义:

struct __wait_queue_head {

spinlock_t  lock;          //自旋锁变量,用于在对等待队列头

struct list_head task_list;  // 指向等待队列的list_head

};

typedef struct __wait_queue_head  wait_queue_head_t;

使用等待队列时首先需要定义一个wait_queue_head,这可以通过DECLARE_WAIT_QUEUE_HEAD宏来完成,这是静态定义的方法。该宏会定义一个wait_queue_head,并且初始化结构中的锁以及等待队列。当然,动态初始化的方法也很简单,初始化一下锁及队列就可以了。

#define DECLARE_WAIT_QUEUE_HEAD(name) \

wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \

.lock                = __SPIN_LOCK_UNLOCKED(name.lock),                \

.task_list        = { &(name).task_list, &(name).task_list } }

将lock赋为unlocked, 将等待队列头指向的等待队列链表指向name,从而将等待队列头和等待队列连起来;

一般在写程序的时候将DECLARE_WAIT_QUEUE_HEAD分成两步来完成:

声明:

wait_queue_head_t  wait_que;

初始化:

init_waitqueue_head( &wait_que);

Linux中等待队列的实现思想如下图所示,当一个任务需要在某个wait_queue_head上睡眠时,将自己的进程控制块信息封装到wait_queue中,然后挂载到wait_queue的链表中,执行调度睡眠。当某些事件发生后,另一个任务(进程)会唤醒wait_queue_head上的某个或者所有任务,唤醒工作也就是将等待队列中的任务设置为可调度的状态,并且从队列中删除。

(2)等待队列中存放的是在执行设备操作时不能获得资源而挂起的进程

定义等待对列:

struct __wait_queue {

unsigned int flags;  //prepare_to_wait()里有对flags的操作,查看以得出其含义

#define WQ_FLAG_EXCLUSIVE        0x01 //一个常数,在prepare_to_wait()用于修改flags的值

void * private          //通常指向当前任务控制块

wait_queue_func_t func;    //唤醒阻塞任务的函数 ,决定了唤醒的方式

struct list_head task_list;    // 阻塞任务链表

};

typedef struct __wait_queue          wait_queue_t;

//声明一个等待队列并初始化为name

#define DECLARE_WAITQUEUE(name, tsk)        \

wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)

#define __WAITQUEUE_INITIALIZER(name, tsk) {        \

.private        = tsk,                                                \

.func                = default_wake_function,                        \

.task_list        = { NULL, NULL } }

//下列两个函数用于对特定的成员进行赋值(当传入不同类型的参数时);

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)

{

q->flags = 0;

q->private = p;          //私有数据指针

q->func = default_wake_function;  //使用默认的唤醒函数

}

static inline void init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func)

{

q->flags = 0;

q->private = NULL;

q->func = func;       // 自定义的唤醒函数

}

(3)对等待队列进行操作

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)  /

{

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

}

//增加一个等待队列new到等待队列头head指向的等待队列链表中;

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

{

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

}

增加一个等待队列到表尾

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

{

list_del(&old->task_list);

}

//wq: 在等待事件的等待队列,condition: 等待的条件

#define __wait_event(wq, condition) \

do {                                                                        \

DEFINE_WAIT(__wait);                \  //定义并初始化一个wait_queue_t结构

for (;;) {                                                        \

prepare_to_wait(&wq, &__wait,TASK_UNINTERRUPTIBLE);        \

if (condition)        \  //看wait_queue:wq要等的condition是否满足

break;                                                \

schedule();        \     //condition不成立,放弃cpu重新调度一个task

}                                                                \

finish_wait(&wq, &__wait);                                        \

} while (0)

上面程序的执行过程:

1.用当前的进程描述块(PCB)初始化一个wait_queue描述的等待任务。

2.在等待队列锁资源的保护下,将等待任务加入等待队列。

3.判断等待条件是否满足,如果满足,那么将等待任务从队列中移出,退出函数。

4.如果条件不满足,那么任务调度,将CPU资源交与其它任务。

5.当睡眠任务被唤醒之后,需要重复(2)、(3)步骤,如果确认条件满足,退出等待事件函数。

我在一个程序中因为使用使用wait_event_interruptible()遇到了很大的麻烦,原因就是不知道condition在函数中具体起个什么作用,通过分析源码终于搞清楚。(后面会有专门的文章来介绍那个问题。)

等待队列编程接口:

wait_event(wq, condition)

这是一个宏,让当前任务处于等待事件状态。输入参数如下:

@wq:等待队列

@conditions:等待条件

wait_event_timeout(wq, condition, timeout)

功能与wait_event类似,多了一个超时机制。参数中多了一项超时时间。

wait_event_interruptible(wq, condition)

这是一个宏,与前两个宏相比,该宏定义的等待能够被消息唤醒。如果被消息唤醒,那么返回- ERESTARTSYS。输入参数如下:

@wq:等待队列

@condition:等待条件

@rt:返回值

wait_event_interruptible_timeout(wq, condition, timeout)

与上一个相比,多了超时机制

wake_up(x)

唤醒等待队列中的一个任务

wake_up_interruptible(x)

用于唤醒wake_event_interruptible()睡眠的进程

wake_up_all(x)

唤醒等待队列中的所有任务

Linux将进程状态描述为如下五种:

TASK_RUNNING:可运行状态。处于该状态的进程可以被调度执行而成为当前进程。

TASK_INTERRUPTIBLE:可中断的睡眠状态。处于该状态的进程在所需资源有效时被唤醒,也可以通过信号或定时中断唤醒(因为有signal_pending()函数)。

TASK_UNINTERRUPTIBLE:不可中断的睡眠状态。处于该状态的进程仅当所需资源有效时被唤醒。

TASK_ZOMBIE:僵尸状态。表示进程结束且已释放资源,但其task_struct仍未释放。

TASK_STOPPED:暂停状态。处于该状态的进程通过其他进程的信号才能被唤醒。

linux 完成量源码,Linux内核中等待队列 和完成量相关推荐

  1. linux下free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  2. linux c free源码,linux命令free源码解读:Procps free.c

    linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...

  3. linux 循环缓冲区 源码,Linux中的循环缓冲区

    在学习到 并发和竞态 时,其中的提到了缓冲区,用于实现免锁算法,这里转载的是大神有关循环缓冲区做的一些操作. 其中源代码在最下面的附件中,有关作者的讲解感觉很清晰,很好,不过这里说一下自己的见解: 点 ...

  4. linux 虚拟文件系统 源码,Linux内核源代码情状分析-虚拟文件系统

    Linux内核源代码情景分析-虚拟文件系统 我们先来看两张图: 第一张是VFS与具体文件系统的关系示意图: 第二张是Linux文件系统的层次结构: 特殊文件:用来实现"管道"的文件 ...

  5. linux usb摄像头 源码,Linux USB摄像头驱动实现源码分析

    Spac5xx的实现是按照标准的USB VIDEO设备的驱动框架编写(其具体的驱动框架可参照/usr/src/linux/drivers/usb/usbvideo.c文件),整个源程序由四个主体部分组 ...

  6. linux 虚拟网卡 源码,Linux的虚拟网卡TUN和TAP

    TUN/TAP 提供了给用户空间程序的包的接收和传输,它可以看成是简单的点对点设备或是 以太网设备.它不是从物理设备接收包,而是从用户空间程序接收包.它发送包不是通过物 理设备来发送包,而是将这些包写 ...

  7. LINUX进程调度分析源码,Linux 实时调度(源码分析)

    为了弄清楚在多cpu系统中是如何实现实时调度的,先引入以下几个概念: cpu的状态: 我们知道,在linux系统中,任务的优先级为0~140. INVALID:(-1)该cpu不可用 IDLE(0): ...

  8. 传奇游戏源码 Linux版本 传奇源码 Linux版 三端源码和搭建, 然后打包生成APP

    此源码牛逼拉萨, 因为鄙人玩了好一段时间, 故此搞篇文章记录下几个技术关键点 Linux架设教程 先决条件: CentOS 7 Nginx 1.8 mysql 5.6 php 5.6 建议使用 IP: ...

  9. linux声卡驱动源码,Linux声卡驱动移植和測试(示例代码)

    一.分析驱动程序,依据开发板改动代码 代码太长,就不贴了,几个注意点: 1. 查看开发板原理图和S3C2410的datasheet,UDA1341的L3MODE.L3DATA.L3CLOCK分别与S3 ...

  10. linux运行geoserver源码,Linux 下Geoserver 的部署

    之前做的是在windows下的Geoserver openlayers 的部署开发 现在需求是将这套系统移植到Linux下,首先先介绍如何在 Linux下部署Geoserver 关于Geoserver ...

最新文章

  1. 按下电源后的几秒钟,CPU 在干嘛?
  2. 电路知识--认识原理图(二)
  3. 企业级应用与互联网应用的区别
  4. 【VirtualBox】VirtualBox的桥接网络模式,为啥网络不稳定?
  5. AE开发右键缩放至图层
  6. android 浏览指定相册,Android -- 采用系统相册浏览指定路径下照片
  7. sql脚本比较大,sqlserver 无法导入,就用cmd命令执行
  8. 3804. 构造字符串-AcWing题库
  9. 【信号与系统】信号频谱和测量之汉明窗
  10. 论文阅读——MobileNetV2: Inverted Residuals and Linear Bottlenecks
  11. Qt引入图标字体包iconfont
  12. html如何将图片弄成背景,如何用css把图片弄成背景
  13. 拜登留学新政:美国读博直接拿绿卡,增加H1B签证限额!但未来留美门槛反而变高了?...
  14. 深圳和信中欧金融科技研究院开业,着力打造金融科技高地项目
  15. 汇编语言简明教程 实验报告
  16. win7 64位VC串口控件打不开解决方法
  17. 关于烛光斧影——赵光义是否谋杀赵匡胤,是否合法继位
  18. 高中英语教师资格证考试经验贴
  19. Android12 新特性及适配指南
  20. M0、M3、M4简单对比

热门文章

  1. 戴尔:未来就绪的IT
  2. 49.Linux/Unix 系统编程手册(下) -- 内存映射
  3. 62. Using Default Magento Cache
  4. 5. Zend_Log
  5. 2. HTML DOM Element 对象
  6. html 可换行属性,html里title属性换行的方法
  7. wex5 导入mysql_wex5 sqllite本地数据库的运用
  8. ascii码与键盘代码的区别
  9. ajax中xmlhttp.readyState==4 xmlhttp.status==200 是什么意思
  10. vue-cli初始化一个项目