`

linux进程的休眠(等待队列)

阅读更多
当进程以阻塞的方式通信,在得到结果前进程会挂起休眠。
为了将进程以一种安全的方式进入休眠,我们需要牢记两条规则:
一、永远不要在原子上下文中进入休眠。
二、进程休眠后,对环境一无所知。唤醒后,必须再次检查以确保我们等待的条件真正为真
简单休眠
完成唤醒任务的代码还必须能够找到我们的进程,这样才能唤醒休眠的进程。需要维护一个称为等待队列的数据结构。等待队列就是一个进程链表,其中包含了等待某个特定事件的所有进程。
linux维护一个“等待队列头”来管理,wait_queue_head_t,定义在<linux/wait.h>
struct  __wait_queue_head {
wq_lock_t  lock;
struct  list_head  task_list;
};
typedef  struct __wait_queue_head  wait_queue_head_t;
初始化方法:
静态方法:
DECLARE_WAIT_QUEUE_HEAD(name)
动态方法:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
linux中最简单的休眠方式是下面的宏,
wait_event(queue, condition)  /*进程将被置于非中断休眠(uninterruptible sleep)*/
wait_event_interruptible(queue, condition) /*进程可被信号中断休眠,返回非0值表示休眠被信号中断*/
wait_event_timeout(queue, condition, timeout)    /*等待限定时间jiffy,condition满足其一返回0*/
wait_event_interruptible_timeout(queue, condition, timeout)
queue是等待队列头,传值方式
condition是任意一个布尔表达式,在休眠前后多次对condition求值,为真则唤醒
唤醒进程的基本函数是wake_up
void wake_up(wait_queue_head_t *queue);    /*唤醒等待在给定queue上的所有进程*/
void wake_up_interruptible(wait_queue_head_t *queue);
实践中,一般是wait_event和wake_up,wait_event_interruptible和wake_up_interruptible成对使用
高级休眠
将进程置于休眠的步骤:
(1)分配和初始化一个 wait_queue_t 结构, 随后将其添加到正确的等待队列
struct __wait_queue {        unsigned int flags;#define WQ_FLAG_EXCLUSIVE       0x01        void *private;        wait_queue_func_t func;        struct list_head task_list;};typedef struct __wait_queue wait_queue_t;(2)设置进程状态,标记为休眠。2.6内核中,使用下面的函数:
      void set_current_state(int new_state);
    在 <linux/sched.h> 中定义有几个任务状态:TASK_RUNNING 意思是进程能够运行。有 2 个状态指示一个进程是   在睡眠: TASK_INTERRUPTIBLE 和 TASK_UNTINTERRUPTIBLE
(3)最后一步是放弃处理器。 但必须先检查进入休眠的条件。如果不做检查会引入竞态: 如果在忙于上面的这个过程时有其他的线程刚刚试图唤醒你,你可能错过唤醒且长时间休眠。因此典型的代码下
if (!condition)
     schedule( );    /*调用调度器,并让出CPU*/
如果代码只是从 schedule 返回,则进程处于TASK_RUNNING 状态。 如果不需睡眠而跳过对 schedule 的调用,必须将任务状态重置为 TASK_RUNNING,还必要从等待队列中去除这个进程,否则它可能被多次唤醒。
手工休眠
上面的进程休眠步骤可通过手工设置:
(1)创建和初始化一个等待队列。常由宏定义完成:
DEFINE_WAIT(my_wait);
name 是等待队列入口项的名字. 也可以用2步来做:
wait_queue_t my_wait;
init_wait(&my_wait);
常用的做法是放一个 DEFINE_WAIT 在循环的顶部,来实现休眠
(2)添加等待队列入口到队列,并设置进程状态:
void prepare_to_wait(wait_queue_head_t *queue,
                               wait_queue_t *wait,
                               int state);
queue 和 wait 分别地是等待队列头和进程入口。state 是进程的新状态:TASK_INTERRUPTIBLE(可中断休眠,推荐)或TASK_UNINTERRUPTIBLE(不可中断休眠,不推荐)
(3)在检查确认仍然需要休眠之后调用 schedule
if (!condition)
     schedule( );    /*调用调度器,并让出CPU*/
(4)schedule 返回,就到了清理时间:
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);
认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue, condition) 底层源码会发现,其实他们只是手工休眠中的函数的组合。所以怕麻烦的话还是用wait_event比较好。
独占等待
当一个进程调用 wake_up 在等待队列上,所有的在这个队列上等待的进程被置为可运行的。 这在许多情况下是正确的做法。但有时,可能只有一个被唤醒的进程将成功获得需要的资源,而其余的将再次休眠。这时如果等待队列中的进程数目大,这可能严重降低系统性能。为此,内核开发者增加了一个“独占等待”选项。它与一个正常的睡眠有 2 个重要的不同:
(1)当等待队列入口设置了 WQ_FLAG_EXCLUSEVE 标志,它被添加到等待队列的尾部;否则,添加到头部。
(2)当 wake_up 被在一个等待队列上调用, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止唤醒.但内核仍然每次唤醒所有的非独占等待。
采用独占等待要满足 2 个条件:
(1)希望对资源进行有效竞争;
(2)当资源可用时,唤醒一个进程就足够来完全消耗资源。
使一个进程进入独占等待,可调用:
void prepare_to_wait_exclusive(wait_queue_head_t *queue, wait_queue_t *wait, int state);
注意:无法使用 wait_event 和它的变体来进行独占等待.
唤醒函数
很少会需要调用wake_up_interruptible 之外的唤醒函数,但为完整起见,这里是整个集合:
wake_up(wait_queue_head_t *queue);
wake_up_interruptible(wait_queue_head_t *queue);
wake_up 唤醒队列中的每个非独占等待进程和一个独占等待进程。wake_up_interruptible 同样, 除了它跳过处于不可中断休眠的进程。它们在返回之前, 使一个或多个进程被唤醒、被调度(如果它们被从一个原子上下文调用, 这就不会发生).
wake_up_nr(wait_queue_head_t *queue, int nr);
wake_up_interruptible_nr(wait_queue_head_t *queue, int nr);
这些函数类似 wake_up, 除了它们能够唤醒多达 nr 个独占等待者, 而不只是一个. 注意传递 0 被解释为请求所有的互斥等待者都被唤醒
wake_up_all(wait_queue_head_t *queue);
wake_up_interruptible_all(wait_queue_head_t *queue);
这种 wake_up 唤醒所有的进程, 不管它们是否进行独占等待(可中断的类型仍然跳过在做不可中断等待的进程)
wake_up_interruptible_sync(wait_queue_head_t *queue);
一个被唤醒的进程可能抢占当前进程, 并且在 wake_up 返回之前被调度到处理器。 但是, 如果你需要不要被调度出处理器时,可以使用 wake_up_interruptible 的"同步"变体. 这个函数最常用在调用者首先要完成剩下的少量工作,且不希望被调度出处理器时。

转自:http://www.cnblogs.com/noaming1900/archive/2011/01/14/1935526.html
分享到:
评论

相关推荐

    Linux的等待队列介绍

    Linux 等待队列是 Linux 操作系统中的一种机制,用于管理和维护一些阻塞(休眠)进程。当一个设备没有可读数据时,读进程可以阻塞,等待写进程写入数据。等待队列是一个进程链表,包含了等待某个特定事件的所有进程...

    Linux进程的睡眠和唤醒

    1 Linux进程的睡眠和唤醒 在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为TASK_RUNNING。一旦一个运行中的进程时间片用完, Linux 内核的调度器会剥夺这个...

    linux 等待队列

    在Linux操作系统中,等待队列(Wait Queue)是一种核心机制,用于实现进程间的同步和通信。等待队列在内核编程中扮演着至关重要的角色,特别是在处理设备驱动、信号量、互斥锁以及I/O操作时。宋宝华的书中详细讲解了...

    嵌入式Linux内核中的等待队列操作.pdf

    在嵌入式Linux内核中,等待队列是实现进程同步和通信的关键机制,它允许进程在等待某个特定事件发生时进入休眠状态,而当该事件发生时,被唤醒继续执行。等待队列的操作涉及到了进程调度、睡眠与唤醒以及内核的并发...

    Linux 进程管理命令

    * WCHAN:正在等待的进程资源 * START:启动进程的时间 * TIME:进程消耗 CPU 的时间 * COMMAND:命令的名称和参数 2.1.2 ps 应用举例 实例一:ps aux [root@localhost ~]# ps -aux | more 可以用 | 管道和 more...

    Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠].pdf

    等待队列是管理休眠进程的关键数据结构,它是一个包含所有等待特定事件的进程链表。在Linux中,等待队列由`wait_queue_head_t`结构体管理,包含一个自旋锁用于保护链表,以及一个`list_head`链表头用于存储等待的...

    Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型I-O和休眠] - Linux设备驱动程序

    3. **等待队列和唤醒机制**:在处理阻塞I/O时,驱动程序会将等待的进程放入等待队列。当设备准备好数据,驱动程序会使用`wake_up_process()`或`wake_up_all()`函数唤醒等待队列上的进程。 4. **同步和异步I/O**:...

    Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠][归类].pdf

    为实现休眠,Linux内核提供了一个称为等待队列的数据结构。等待队列由`wait_queue_head_t`结构体管理,它包含一个自旋锁和一个链表,链表中存储了等待特定事件的所有进程。`wait_queue_head_t`结构允许内核跟踪哪些...

    linux进程调度与进程切换1

    本篇文章主要探讨Linux进程调度的两个主要类型——主动调度和被动调度,以及涉及的关键函数和流程。 1. 主动调度: 主动调度是指进程在运行过程中因自身行为触发的调度,比如进程调用`sleep()`或`exit()`函数进入...

    Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型IO和休眠][定义].pdf

    当进程休眠时,它会被从运行队列中移除,不再参与CPU时间片的分配。休眠有两条重要的规则: 1. 不要在持有原子上下文(如自旋锁、seqlock或RCU锁)或关闭中断时休眠,因为这可能导致死锁或其他同步问题。 2. 进程被...

    9.阻塞IO1

    总结来说,阻塞IO模型在Linux中通过等待队列实现了进程在设备数据未准备好的时候的休眠和唤醒。这种模型简单直观,但在高并发环境下可能会导致CPU资源浪费,因为进程在等待期间无法执行其他任务。因此,非阻塞IO和...

    Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型IO和休眠]参考.pdf

    为了跟踪休眠的进程,Linux内核使用了等待队列(wait_queue_head_t)。这是一个包含所有等待特定事件的进程链表。当进程准备休眠时,它会被添加到相应的等待队列中。当事件发生时,通过调用`wake_up`或`wake_up_...

    Linux进程管理命令详解.pdf

    sleep命令的作用是,使当前进程休眠一段时间。sleep命令的使用权限是所有用户。sleep命令的格式为:sleep time,time是休眠的时间,可以是seconds、minutes、hours、days或weeks为单位。 14.nohup命令 nohup命令的...

    Linux设备驱动程式学习(5)-高级字符驱动程式操作[(2)阻塞型IO和休眠][汇编].pdf

    总结起来,Linux设备驱动程序中的高级字符驱动操作涉及阻塞型I/O和休眠机制,通过等待队列和相关宏与函数,实现进程在无法立即响应请求时的优雅处理。理解并熟练掌握这些机制对于编写高效、可靠的设备驱动至关重要。

    Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠][参照].pdf

    在Linux内核中,休眠的进程会被从运行队列中移除,直到被唤醒。休眠的安全性需要遵循两个关键原则: 1. **避免在原子上下文中休眠**:当驱动程序持有自旋锁、seqlock或RCU锁时,绝对不允许休眠。这是因为这些锁假设...

    Linux 进程相关命令

    ps ps 命令用于查看系统中的进程状态,格式为“ps [参数]”。 ps 命令的参数以及作用: 参数 ...**R(运行):**进程正在运行或在运行队列中等待。 **S(中断):**进程处于休眠中,当某个条件形

    Linux进程间通信-命名管道实例.pdf

    在给定的文件“Linux进程间通信-命名管道实例.pdf”中,我们可以通过两个示例程序来理解命名管道的工作原理。 首先,我们来看写操作程序。这个程序主要负责向命名管道写入数据。在程序中,定义了一个常量`FIFO_...

    Linux2.6 进程调度机制的剖析

    - 进程主动进入休眠状态,也会引发调度。 ##### 调度策略 在Linux2.6中,保留了三种调度策略:SCHED_NORMAL用于普通进程,而SCHED_FIFO和SCHED_RR则分别用于不同类型的实时进程调度。SCHED_FIFO下,实时进程将独占...

    linux设备驱动中的阻塞与非阻塞IO.pdf

    每当条件达成,比如资源就绪,等待队列中的所有进程都将被唤醒,从而无需轮询或持续检查资源状态。 等待队列的使用场景与中断类似,都是基于事件驱动的模型,一旦特定事件发生,如设备就绪,就会自动通知等待的进程...

    Linux系统中进程和线程的通信方式总结

    - 条件变量(ConditionVariable):条件变量与互斥锁配合使用,允许线程因为某些条件未达成而进入休眠,直到其他线程改变条件,并发出信号唤醒等待该条件的线程。 - 读写锁(Read-WriteLock):读写锁允许多个线程...

Global site tag (gtag.js) - Google Analytics