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等待队列与唤醒的更多相关文章

  1. Linux等待队列(Wait Queue)

    1. Linux等待队列概述 Linux内核的等待队列(Wait Queue)是重要的数据结构,与进程调度机制紧密相关联,可以用来同步对系统资源的访问.异步事件通知.跨进程通信等.在Linux中,等待 ...

  2. linux驱动程序之电源管理之标准linux休眠和唤醒机制分析(二)

    三.pm_test属性文件读写 int pm_test_level = TEST_NONE; static const char * const  pm_tests[__TEST_AFTER_LAST ...

  3. Linux使用wake_up_interruptible()唤醒注册到等待队列上的进程

    http://blog.sina.com.cn/s/blog_4770ef020101h48l.html     功能:唤醒注册到等待队列上的进程 原型:     #include     void ...

  4. linux驱动程序之电源管理之标准linux休眠与唤醒机制分析(一)

    1. Based on linux2.6.32,  only for mem(SDR) 2. 有兴趣请先参考阅读: 电源管理方案APM和ACPI比较.doc Linux系统的休眠与唤醒简介.doc 3 ...

  5. linux驱动程序之电源管理 之linux休眠与唤醒(2)

    在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务:(2)调用注册的设备的suspend的回调函数:(3)按照注册顺序休眠核心设备和使CPU进入休眠态.       冻结进程是 ...

  6. Android后台的linux一直保持唤醒状态,不进入睡眠

    由于要做Android手机的电池续航测试,是不能插usb的,所以把case放到sh文件中,之后push到手机里,执行的. 但是出现个问题,假如case中有很长时间的sleep操作,关闭手机屏幕,这样l ...

  7. Linux学习-可唤醒停机期间的工作任务

    什么是 anacron anacron 并不是用来取代 crontab 的,anacron 存在的目的就在于我们上头提到的,在处理非 24 小 时一直启动的 Linux 系统的 crontab 的执行 ...

  8. 【Linux驱动】内核等待队列

    在Linux中, 一个等待队列由一个"等待队列头"来管理,等待队列是双向链表结构. 应用场合:将等待同一资源的进程挂在同一个等待队列中. 数据结构 在include/linux/w ...

  9. Linux内核等待队列

    在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列可看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待等列中取出进程. Linux 2.6内核提供了如下关于 ...

随机推荐

  1. Bridges Gym - 100712H  无向图的边双连通分量,Tarjan缩点

    http://codeforces.com/gym/100712/attachments 题意是给定一个无向图,要求添加一条边,使得最后剩下的桥的数量最小. 注意到在环中加边是无意义的. 那么先把环都 ...

  2. idea获取激活码

    访问地址拿到激活码:http://idea.lanyus.com/getkey

  3. ubuntu触摸板关闭开启

    sudo rmmod psmouse          #用来禁用触摸板 sudo modprobe psmouse     #用来启用触摸板

  4. 174. 删除链表中倒数第n个节点

    描述 笔记 数据 评测 给定一个链表,删除链表中倒数第n个节点,返回链表的头节点. 注意事项 链表中的节点个数大于等于n 您在真实的面试中是否遇到过这个题? Yes 样例 给出链表1->2-&g ...

  5. 死磕 java并发包之AtomicStampedReference源码分析(ABA问题详解)

    问题 (1)什么是ABA? (2)ABA的危害? (3)ABA的解决方法? (4)AtomicStampedReference是什么? (5)AtomicStampedReference是怎么解决AB ...

  6. js实现接口的几种方式

    Javascript模仿接口可以有三种方式:1.注释法 2.检查属性法 3.鸭式辨形法 1.注释法:此方法属于程序文档范畴,对接口的继承实现完全依靠程序员自觉 /* interface People{ ...

  7. 关于如何用jq定位到某个元素的索引

    在点击事件触发时候,通常如果有同样的className的列表我们都需获取它的索引来知道到底点击的是那一个 $('.info_content').mousemove(function(){ var ro ...

  8. jmeter中通过命令方式生成结果文件

    通过命令的方式将jmeter生成的jtl结果文件生成html文件,以便更直观的分析结果数据,以下命令可以放在1个bat文件中取执行. bat文件可以放到jmeter的根目录下. 步骤1: 通过命令方式 ...

  9. UVA 11324 The Largest Clique (强连通分量,dp)

    给出一个有向图,求一个最大的结点集合,任意两个点u,v.u可到达v或v可到达u. 一个强连通分量肯定一起选的.而且只能在一条路径上. 所以先找出所有scc,然后缩点找一条最大权的路径,按拓扑序跑DAG ...

  10. Sublime 设置移动光标快捷键

    发现问题 在使用Sublime和其他编辑器一个很不爽的问题是:在输入一个函数或者有左右的符号或者在引号内时,总是要跳出来,无奈方向键又在主键盘的另一边,只能不断的切换,太特么操蛋,就不能让手指不离开主 ...