linux wake_up 相关函数

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。等待队列由函数内部自己实现。

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值