Linux 进程等待队列【转】
本文转载自:http://blog.csdn.net/dlutbrucezhang/article/details/9212067
Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。
在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为"连接件"。它通过一个双链表和把等待tast的头,和等待的进程列表链接起来。从上图可以清晰看到。所以我们知道,如果要实现一个等待队列,首先要有两个部分。队列头和队列项。下面看他们的数据结构。
- <span style="font-size:18px;"><strong>struct list_head {
- struct list_head *next, *prev;
- };
- struct __wait_queue_head {
- spinlock_t lock;
- struct list_head task_list;
- };
- typedef struct __wait_queue_head wait_queue_head_t;
- struct __wait_queue {
- unsigned int flags;
- #define WQ_FLAG_EXCLUSIVE 0x01
- void *private;//2.6版本是采用void指针,而以前的版本是struct task_struct * task;
- //实际在用的时候,仍然把private赋值为task
- wait_queue_func_t func;
- struct list_head task_list;
- };
- </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);
- <span style="font-size:18px;"><strong>#define DECLARE_WAITQUEUE(name, tsk) /
- wait_queue_t name =__WAITQUEUE_INITIALIZER(name, tsk)
- #define __WAITQUEUE_INITIALIZER(name, tsk) { task: tsk, task_list: { NULL, NULL }, __WAITQUEUE_DEBUG_INI(name)}
- </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()宏:
- <span style="font-size:18px;"><strong>#define wait_event(wq, condition) /
- do { /
- if (condition) /
- break; /
- __wait_event(wq, condition); /
- } while (0)
- #define __wait_event_timeout(wq, condition, ret) /
- do { /
- DEFINE_WAIT(__wait); /
- /
- for (;;) { /
- prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); /
- if (condition) /
- break; /
- ret = schedule_timeout(ret); /
- if (!ret) /
- break; /
- } /
- finish_wait(&wq, &__wait); /
- } while (0)
- </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()函数。进程在睡眠中可能在等待的时间没有到达就被信号打断而被唤醒,也可能是等待的时间到达而被唤醒。
Linux 进程等待队列【转】的更多相关文章
- Linux 进程等待队列
Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制. 在这个链表中,有两种数据结构:等待队列头(wait_queue_head_t)和等待 ...
- Linux进程管理知识整理
Linux进程管理知识整理 1.进程有哪些状态?什么是进程的可中断等待状态?进程退出后为什么要等待调度器删除其task_struct结构?进程的退出状态有哪些? TASK_RUNNING(可运行状态) ...
- Linux进程的睡眠和唤醒简析
COPY FROM:http://www.2cto.com/os/201204/127771.html 1 Linux进程的睡眠和唤醒 在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在 ...
- linux进程模型总结
Linux进程通过一个task_struct结构体描述,在linux/sched.h中定义,通过理解该结构,可更清楚的理解linux进程模型. 包含进程所有信息的task_struct数据 ...
- linux进程解析--进程的创建
通常我们在代码中调用fork()来创建一个进程或者调用pthread_create()来创建一个线程,创建一个进程需要为其分配内存资源,文件资源,时间片资源等,在这里来描述一下linux进程的创建过程 ...
- Linux进程模型
----原文链接:http://www.cnblogs.com/biyeymyhjob/archive/2012/08/01/2617884.html------ Linux进程通过一个task_st ...
- Linux唤醒抢占----Linux进程的管理与调度(二十三)
1. 唤醒抢占 当在try_to_wake_up/wake_up_process和wake_up_new_task中唤醒进程时, 内核使用全局check_preempt_curr看看是否进程可以抢占当 ...
- Linux进程管理 (2)CFS调度器
关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...
- Linux中等待队列的实现
1. 等待队列数据结构 等待队列由双向链表实现,其元素包括指向进程描述符的指针.每个等待队列都有一个等待队列头(wait queue head),等待队列头是一个类型为wait_quequ ...
随机推荐
- ado:SqlDataAdapter的两种不同写法,以及SqlCommand的两种不同写法
原文发布时间为:2008-08-01 -- 来源于本人的百度文章 [由搬家工具导入] SqlDataAdapter:(它是自动打开连接且自动关闭的,所以可以不必显示打开关闭连接) SqlConnect ...
- linux网络性能评估
Linux网络性能评估 参考自:自学it网,http://www.zixue.it/. 网络性能评估(1)通过ping命令检测网络的连通性.(2)通过netstat -i 组合检测网络接口状况.(3) ...
- SGU112
题意:求a^b-b^a次,100以内.大数的-和*的模拟,用的模板,注意该模板中间和结果都不能出现负数. #include<iostream> #include<string> ...
- HUNAN 11560 Yangyang loves AC(二分+贪心)
http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11560&courseid=0 题意:总共有n天,每天 ...
- Centos7安装完成后一些基本操作
1.基本操作一:主机名 # centos7有一个新的修改主机名的命令hostnamectl hostnamectl set-hostname --static www.node1.com # 有些命令 ...
- LeetCode第一题以及时间复杂度的计算
问题描述:给定一组指定整数数组,找出数组中加和等于特定数的两个数. 函数(方法)twoSum返回这两个数的索引,index1必须小于index2. 另外:你可以假设一个数组只有一组解. 一个栗子: I ...
- Spring基于Setter函数的依赖注入(DI)
以下内容引用自http://wiki.jikexueyuan.com/project/spring/dependency-injection/spring-setter-based-dependenc ...
- 异常来自 HRESULT:0x800A01A8
Windows 10 Enterprise Microsoft Office 2013 – Excel Oracle BI Publisher Desktop 11.1.1.7 异常来自 HRESUL ...
- Activiti Model Editor组件
通过Activiti Modeler架构图可知,Activiti Explorer采用的是Vaadin框架. Vaadin 是一种 Java Web 应用程序的开发框架, 其设计目标是便利地创建和维护 ...
- flask可以通过缓存模板或者页面达到性能提升
flask可通过插件flask-cache缓存页面,或者把模板缓存到memcache里,增加访问速度. 前提是:页面不是频繁变化的.如果你的访问量很大的话,哪怕缓存一两分钟也会大大的提高性能的 Fla ...