Linux 进程控制——等待队列详解

一、什么是睡眠


    对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状态并且从调度器的运行队列中去除. 直到发生某些事情改变了那个状态, 这个进程将不被在任何 CPU 上调度, 并且, 因此, 将不会运行. 一个睡着的进程已被搁置到系统的一边, 等待以后发生事件.

    LDD3说得很玄乎,睡眠是“自愿调度”,其实就是将当前进程的状态设置为 TASK_INTERRUPTIBLE 等状态,然后schedule() 让出CPU1,让调度器重新选择一个进程来执行。

    对于一个 Linux 驱动使一个进程睡眠是一个容易做的事情. 但是, 有几个规则必须记住以安全的方式编码睡眠.

这些规则的第一个是: 当你运行在原子上下文时不能睡眠.  

    一个另外的相关的点, 当然, 是你的进程不能睡眠除非确信其他人, 在某处的, 将唤醒它.做唤醒工作的代码必须也能够找到你的进程来做它的工作. 确保一个唤醒发生, 是深入考虑你的代码和对于每次睡眠, 确切知道什么系列的事件将结束那次睡眠.使你的进程可能被找到, 真正地, 通过一个称为等待队列的数据结构实现的. 一个等待队列就是它听起来的样子:一个进程列表, 都等待一个特定的事件.

二、如何睡眠

    在 Linux 中, 一个等待队列由一个"等待队列头"来管理, 一个 wait_queue_head_t 类型的结构, 定义在<linux/wait.h>中. 一个等待队列头可被定义和初始化, 使用:

    DECLARE_WAIT_QUEUE_HEAD(name); 
    或者动态地, 如下:

    wait_queue_head_t my_queue;
    init_waitqueue_head(&my_queue);

  1、简单睡眠    

    Linux 内核中睡眠的最简单方式是一个宏定义, 称为 wait_event(有几个变体); 它结合了处理睡眠的细节和进程在等待的条件的检查. wait_event 的形式是:

    wait_event(queue, condition)
    wait_event_interruptible(queue, condition)
    wait_event_timeout(queue, condition, timeout)
    wait_event_interruptible_timeout(queue, condition, timeout)
    这些东西如何使用?queue 是等待队列头,condition 是条件,如果调用 wait_event 前 condition == 0 ,则调用 wait_event 之后,当前进程就会休眠。
    那么它们四个又有什么不同?
    wait_event:将当前进程的状态设置为 TASK_UNINTERRUPTIBLE  ,然后 schedule()
    wait_event_interruptible:         TASK_INTERRUPTIBLE    ,然后 schedule()
    wait_event_timeout:               TASK_UNINTERRUPTIBLE  ,然后 schedule_timeout()
    wait_event_interruptible_timeout:  TASK_INTERRUPTIBLE    , 然后 schedule_timeout()
    TASK_INTERRUPTIBLE 与 TASK_UNINTERRUPTIBLE 区别在于,它的休眠是否会被信号打断,别的进程发来一个信号比如 kill ,TASK_INTERRUPTIBLE 就会醒来去处理。然而 TASK_UNINTERRUPTIBLE 不会。schedule(),进程调度,而schedule_timeout()进行调度之后,一定时间后自动唤醒。
    对应于不同的进程状态,使用不同的唤醒函数:
    void wake_up(wait_queue_head_t *queue);
    void wake_up_interruptible(wait_queue_head_t *queue);
    唤醒时很有意思,比如你调用 wake_up 去唤醒一个使用 wait_event 等,进入休眠的进程,唤醒之后,它会判断 condition 是否为真,如果还是假的继续睡眠。至于为什么这个样子,后面分析代码就会明白。
  
  2、手动睡眠
    DECLARE_WAITQUEUE(name, tsk)  创建一个等待队列:
        tsk一般为当前进行current. 这个宏定义并初始化一个名为name的等待队列.
    将等待队列头 加入/移除 等待队列:
        void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
        void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);
        void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
    设置进程状态:

        set_current_state(TASK_INTERRUPTIBLE) 等
    进程调度:  
        schedule() 或者 schedule_timeout()

三、内核如何实现

    以 wait_event 为例,我们看看内核都干了些什么。
————————————————
版权声明:本文为CSDN博主「Linux学习之路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lizuobin2/article/details/51785812

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值