linux wake_up 相关函数

来源:互联网 发布:淘宝充值平台能赚钱吗 编辑:程序博客网 时间:2024/06/05 23:52

linux 中有两种wake_up函数.第一种是wake_up_xxx,第二种是wake_up_interruptible_xxx.

1.wake_up_xxx 函数如下:

#define wake_up(x)          __wake_up(x, TASK_NORMAL, 1, NULL)#define wake_up_nr(x, nr)       __wake_up(x, TASK_NORMAL, nr, NULL)#define wake_up_all(x)          __wake_up(x, TASK_NORMAL, 0, NULL)#define wake_up_locked(x)       __wake_up_locked((x), TASK_NORMAL, 1)#define wake_up_all_locked(x)       __wake_up_locked((x), TASK_NORMAL, 0)

TM被骗了,这不是函数,而是一堆宏.因为习惯问题,正文中我还是叫它们函数吧.
wake_up_xxx函数可以唤醒以下函数(或宏)导致的等待.

sleep_on(wait_queue_head_t *q)sleep_on_timeout(wait_queue_head_t *q, long timeout)wait_event(wq, condition)wait_event_timeout(wq, condition, timeout)

那么以上几个wake_up函数有什么区别呢?
本文假设你已经知道wake_up()函数是用来唤醒等待队列的.只是不清楚以上几个wake_up函数具体的区别.如是这样,见下文:
wake_up         :只唤醒等待队列中第一个等待的线程.
wake_up_nr       :唤醒等待队列中从队列头开始的nr个等待的线程.
wake_up_all       :唤醒等待队列中所有等待的线程.
wake_up_locked     :同wake_up(),区别在于此函数用于在调用此函数时,用户已经手动 spin_lock了此等待队列头的             lock,(这个函数存在的原因是同一个spin_lock在同一个线程中不能递归加锁.)
wake_up_all_locked   :这个函数的作用我想聪明的你已经猜到了.

提外话1.wake_up想唤醒几个队列就唤醒几个,是如果实现的呢?
wake_up调用流程如下:
wake_up(x) ->__wake_up(x, TASK_NORMAL, 1, NULL)->__wake_up_common();
由上面的调用流程可知,wake_up最终会调用到__wake_up_common函数,而此函数的第三个参数就是指明需要唤醒此等待队列的多少个线程.见__wake_up_common函数的if break条件中用到了nr_exclusive。

static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,            int nr_exclusive, int wake_flags, void *key){    wait_queue_t *curr, *next;    list_for_each_entry_safe(curr, next, &q->task_list, task_list) {        unsigned flags = curr->flags;        if (curr->func(curr, mode, wake_flags, key) &&                (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)            break;    }}

提外话2.看起来wake_up_all是不是比wake_up要牛逼,因为wake_up_all可以唤醒这个等待队列上的所有等待线程,wake_up只能唤醒一个
非也。
第一、在我们自己创建工作队列的用法来看,90%的情况下,工作队列中只有一个等待的线程。(不信看内核源码)
第二、即使同时唤醒多个线程,由于CPU不够用,或唤醒的几个线程由于需要访问共享临界区资源,而最终只用一个线程能运行。

2.wake_up_interruptible_xxx 函数如下

#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)#define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)#define wake_up_interruptible_sync(x)   __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)

对应的,wake_up_interruptible_xxx函数可以唤醒以下函数(或宏)导致的等待

interruptible_sleep_on(wait_queue_head_t *q)interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout)wait_event_interruptible(wq, condition)wait_event_interruptible_timeout(wq, condition, timeout)

发现没,wake_up唤醒wait_event,wake_up_interruptible唤醒wait_event_interruptible,这也许就是linux中的对称美。

wake_up_interruptible_xxx函数与wake_up_xxx函数功能类似。
只是wait_event_xxx与sleep_on_xxx函数进入等待队列后,不能被信号唤醒.
而wait_event_interruptible_xxx 与interruptible_sleep_on_xxx函数进入等待队列后,可以被信号唤醒。

题外话3. 既然等等了为什么还要唤醒,是什么信号唤醒它

这个问题曾经困扰了我好久,直到这几天看了网上以下这篇文章才焕然大悟。
(signal_pending函数解析)http://blog.csdn.net/tommy_wxie/article/details/12448935#

我的理解是:
1、当驱动正在执行一个阻塞操作时,如果不能被信号唤醒那么,这个时候用户按下ctrl+c将无法结束程序。
而如果是可唤醒的阻塞,则当用户按下ctrl+c,或向这个进程发送了一个信号时,内核会退出阻塞,先处理用户的信号响应。如果这时是在系统调用过程中阻塞,则当处理完信号响应后,系统会再次调用此系统调用。所以我们才会在驱动中看到可被interruptible的等待函数中在阻塞退出后,会执行如下代码。
这段代码就是在检查此次退出阻塞是因为有人wake_up了还是有信号需要到来需要处理。
当signal_pending(current)返回为true时,表示是有信号需要处理。

    if (signal_pending(current)) {     ret = -ERESTARTSYS;     return ret;    }

题外话4.为什么将sleep_on_xxx,与wait_event_xxx函数发在一起
原因我认为他们有以下几个共同点:
1、都是在一个自定义的等待队列头上等待,也都是通过这个等待队列来唤醒进程。
2、使用时用户只需要定义等待队列头wait_queue_head_t,而不需要定义等待队列wait_queue_t。等待队列由函数内部自己实现。

阅读全文
0 0