Linux等待队列与唤醒
1.数据结构
1.1等待队列头
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;
初始化等待队列头
#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
.lock = __SPIN_LOCK_UNLOCKED(name.lock), \
.task_list = { &(name).task_list, &(name).task_list } } #define DECLARE_WAIT_QUEUE_HEAD(name) \
wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
1.2等待队列
typedef struct __wait_queue wait_queue_t;
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key);
int default_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key); struct __wait_queue {
unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
void *private;
wait_queue_func_t func;
struct list_head task_list;
};
初始化等待队列
#define __WAITQUEUE_INITIALIZER(name, tsk) { \
.private = tsk, \
.func = default_wake_function, \
.task_list = { NULL, NULL } } #define DECLARE_WAITQUEUE(name, tsk) \
wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
等待队列的task_list加入等待队列头的task_list链表。一般将wait_queue_func_t赋值为下面的默认处理函数:
int default_wake_function(wait_queue_t *curr, unsigned mode, int sync,
void *key)
{
return try_to_wake_up(curr->private, mode, sync);
}
1.3添加/删除等待队列
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
{
list_add(&new->task_list, &head->task_list);
} static inline void __remove_wait_queue(wait_queue_head_t *head,
wait_queue_t *old)
{
list_del(&old->task_list);
}
2等待事件
调用以下四个宏等待事件,等待以第一个参数作为等待队列头的等待队列被唤醒,第二个参数condition必须被满足,否则继续阻塞。
wait_event()和wait_event_interruptible()的区别是后者可以被信号打断,而前者不能。
加上timeout后的宏意味着阻塞等待的超时时间,以jiffy为单位,在timeout到达时,不论condition是否满足,均返回。
#define wait_event(wq, condition)
#define wait_event_timeout(wq, condition, timeout)
#define wait_event_interruptible(wq, condition)
#define wait_event_interruptible_timeout(wq, condition, timeout)
下面以wait_event()为例分析执行过程
#define wait_event(wq, condition) \
do { \
if (condition) \
break; \
__wait_event(wq, condition); \
} while ()
#define __wait_event(wq, condition) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) \
break; \
schedule(); \
} \
finish_wait(&wq, &__wait); \
} while ()
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)宏展开如下
#define DEFINE_WAIT(name) \
wait_queue_t name = { \
.private = current, \
.func = autoremove_wake_function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}
int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
int ret = default_wake_function(wait, mode, sync, key); if (ret)
list_del_init(&wait->task_list);
return ret;
}
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()为例分析,
wake_up(x)
-->__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, , NULL)
-->__wake_up_common(q, mode, nr_exclusive, , key);//唤醒等待队列头链表中的所有等待队列
关键在于__wake_up_common()函数
/*
* The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just
* wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve
* number) then we wake all the non-exclusive tasks and one exclusive task.
*
* There are circumstances in which we can try to wake a task which has already
* started to run but is not in state TASK_RUNNING. try_to_wake_up() returns
* zero in this (rare) case, and we handle it by continuing to scan the queue.
*/
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int sync, void *key)
{
struct list_head *tmp, *next; list_for_each_safe(tmp, next, &q->task_list) {
wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list);
unsigned flags = curr->flags; if (curr->func(curr, mode, sync, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
其中的curr->func即指向autoremove_wake_function()函数。
唤醒进程是在try_to_wake_up(curr->private, mode, sync)函数中具体执行的,唤醒较复杂,后面有时间在分析。
可以简单理解为执行完该函数,2中wait_event()挂起的等待队列就被唤醒回来继续执行了。
Linux等待队列与唤醒的更多相关文章
- Linux等待队列(Wait Queue)
1. Linux等待队列概述 Linux内核的等待队列(Wait Queue)是重要的数据结构,与进程调度机制紧密相关联,可以用来同步对系统资源的访问.异步事件通知.跨进程通信等.在Linux中,等待 ...
- linux驱动程序之电源管理之标准linux休眠和唤醒机制分析(二)
三.pm_test属性文件读写 int pm_test_level = TEST_NONE; static const char * const pm_tests[__TEST_AFTER_LAST ...
- Linux使用wake_up_interruptible()唤醒注册到等待队列上的进程
http://blog.sina.com.cn/s/blog_4770ef020101h48l.html 功能:唤醒注册到等待队列上的进程 原型: #include void ...
- linux驱动程序之电源管理之标准linux休眠与唤醒机制分析(一)
1. Based on linux2.6.32, only for mem(SDR) 2. 有兴趣请先参考阅读: 电源管理方案APM和ACPI比较.doc Linux系统的休眠与唤醒简介.doc 3 ...
- linux驱动程序之电源管理 之linux休眠与唤醒(2)
在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务:(2)调用注册的设备的suspend的回调函数:(3)按照注册顺序休眠核心设备和使CPU进入休眠态. 冻结进程是 ...
- Android后台的linux一直保持唤醒状态,不进入睡眠
由于要做Android手机的电池续航测试,是不能插usb的,所以把case放到sh文件中,之后push到手机里,执行的. 但是出现个问题,假如case中有很长时间的sleep操作,关闭手机屏幕,这样l ...
- Linux学习-可唤醒停机期间的工作任务
什么是 anacron anacron 并不是用来取代 crontab 的,anacron 存在的目的就在于我们上头提到的,在处理非 24 小 时一直启动的 Linux 系统的 crontab 的执行 ...
- 【Linux驱动】内核等待队列
在Linux中, 一个等待队列由一个"等待队列头"来管理,等待队列是双向链表结构. 应用场合:将等待同一资源的进程挂在同一个等待队列中. 数据结构 在include/linux/w ...
- Linux内核等待队列
在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待等列中取出进程. Linux 2.6内核提供了如下关于 ...
随机推荐
- Codeforces 1142E(图、交互)
题目传送 官方题解说的很好了,剩下的就是读大佬代码了,前面是tarjan求SCC缩点图.我图论没学过,接下来删点是怎么操作看得有点头秃,直到我看到了%%%安德鲁何神仙的代码. 按照题面连通紫线以后,我 ...
- js判断网页访问设备类型
有时候我们会需要来根据不同的设备访问进行不同的操作,在网上找了一下,主要是根据Navigator对象, if(/Android|Windows Phone|webOS|iPhone|iPod|Blac ...
- Sql Server 排序规则字符集的冲突问题
可通过如下sql 进行修改: 如果整个DB都不一致: Alter database Expense_Portal collate Chinese_PRC_CI_AS 某张Table的栏位不一致: ) ...
- JS的文本框验证以及form表单的提交阻止
js: 1.只能输入数字 只能输入数字:<input type="text" onkeyup="javascript:ReNumber(this)" /& ...
- [BZOJ2434][Noi2011]阿狸的打字机 AC自动机+树状数组+离线
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2434 题目中这种多个串匹配的问题,一下子就想到了AC自动机.然后发现如果要建立AC自动机, ...
- datagrid数据网格获取所有选中行的索引,插入某个列值为其他列的运算值
获取所有选中行的索引,存入数组ary中: var data=$("#dg").datagrid("getSelections"); var ary=[]; fo ...
- Android View 背景选择器编写技巧
在项目中选择器的使用是非常多的,以下是本人在项目中的一些常用的背景选择器的写法 带边框下划线背景选择器效果图: 上面布局中放了10个CheckBox,然后设置了CheckBox的背景图片位,背景选择器 ...
- MyEclipse中把java项目打包——含有第三方jar包【转】
也适用于eclipse导出jar. 在将项目打包为jar包时一直出现“ClassNotDefFound”错误,百度了很多解决办法都没有解决.最终找到一个很好的解决办法. 1.打包步骤 (1)右键单击j ...
- MySQL存储过程(更新指定字段的数据)
mysql存储过程示例: USE 数据库名称;DROP PROCEDURE IF EXISTS 数据库名称.存储过程名称;delimiter $$CREATE PROCEDURE 数据库名称.存储过程 ...
- jmeter中通过jdbc方式连接mysql数据库的配置参考
jmeter中通过jdbc方式连接mysql数据库的配置参考: Database URL=jdbc:mysql://ip:port/dbname?useUnicode=true&allowMu ...