当进程以阻塞的方式通信,在得到结果前进程会挂起休眠。
为了将进程以一种安全的方式进入休眠,我们需要牢记两条规则:
一、永远不要在原子上下文中进入休眠。
二、进程休眠后,对环境一无所知。唤醒后,必须再次检查以确保我们等待的条件真正为真

简单休眠
完成唤醒任务的代码还必须能够找到我们的进程,这样才能唤醒休眠的进程。需要维护一个称为等待队列的数据结构。等待队列就是一个进程链表,其中包含了等待某个特定事件的所有进程。
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 的"同步"变体. 这个函数最常用在调用者首先要完成剩下的少量工作,且不希望被调度出处理器时。

Linux进程休眠和唤醒相关推荐

  1. 标准linux休眠和唤醒机制分析

    标准linux休眠和唤醒机制分析 标准linux休眠和唤醒机制分析(一) 说明: 1. Based on linux2.6.32, only for mem(SDR) 2. 有兴趣请先参考阅读:电源管 ...

  2. 标准linu休眠和唤醒机制分析(一)

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

  3. Linux Kernel and Android 休眠与唤醒(中文版)

    Linux Kernel and Android 休眠与唤醒(中文版) 四月 18th, 2010 0 Comments/1664 hits Table of Contents 简介 国际化 版本信息 ...

  4. Linux Kernel and Android休眠与唤醒

    版本信息 Linux Kernel: v2.6.28      Android: v2.0 对于休眠(suspend)的简单介绍 在Linux中,休眠主要分三个主要的步骤: 版本信息 Linux Ke ...

  5. linux查看睡眠进程,关于 Linux 进程的睡眠和唤醒 ,来看这篇就够了~

    1 Linux 进程的睡眠和唤醒 在 Linux 中,仅等待 CPU 时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态标志位为 TASK_RUNNING.一旦一个运行中的进程时 ...

  6. linux待机唤醒_Linux电源管理-休眠与唤醒

    1.休眠方式 在内核中,休眠方式有很多种,可以通过下面命令查看 # cat /sys/power/state //来得到内核支持哪几种休眠方式. 常用的休眠方式有freeze,standby, mem ...

  7. 进程的休眠与唤醒(等待队列)

    1.进程休眠 (1)进程有三种基本状态:就绪态.阻塞态.运行态. <1>阻塞态:进程缺少除了CPU之外的某些资源,因此该进程不能被运行,被阻塞住了不能被CPU调度: <2>就绪 ...

  8. Linux的电源管理-休眠与唤醒

    写在前面 为了理清新平台系统休眠和唤醒的流程,通过学习其他平台的电源管理方法,曲径通幽, 达到目的. 刚接手新平台,且相应的资料不多,很容易让人力不从心;我在网上寻找了学习资源,发现韦东山对S3C24 ...

  9. Linux 系统如何找到唤醒某个进程的源头

    进程唤醒源 顾名思义:指导致进程从阻塞状态(等待.休眠)转换到就绪状态的事件或条件.当进程在等待某个事件发生时,它会进入阻塞状态.一旦等待的事件发生,进程就会被唤醒并转换到就绪状态,等待操作系统调度器 ...

最新文章

  1. 用栈来表示队列,用队列来表示栈
  2. [LintCode] Reverse Integer
  3. ASP.NET MVC 4使用PagedList.Mvc分页
  4. CodeIgniter框架下载辅助函数的一个小bug
  5. 【广告技术】揭秘!腾讯广告是如何有效划分用户群体的
  6. 如何优雅地(用TeX)写AI论文
  7. 判断一个点是否在多边形区域内
  8. iOS视频播放器开发
  9. linux服务器怎么安装360杀毒软件,360主机卫士Linux版使用安装教程
  10. 汽车变排量空调压缩机电磁阀故障诊断
  11. Three.js星空粒子特效
  12. python语料库是什么,Python 语料库的搭建
  13. python 操作微信_利用 Python 实现微信半自动化操作
  14. iOS 教你如何修改微信运动步数
  15. Docker的常用命令大全
  16. 【记第一次kaggle比赛】PetFinder.my - Pawpularity Contest 宠物预测
  17. mysql备份恢复与集群部署
  18. C语言 带参数宏定义中 # 和 ## 知识点总结、代码分析
  19. 苏宁易购网址爬虫爬取商品信息及图片
  20. ORACLE之RAC搭建过程9-配置grid和oracle用户互信

热门文章

  1. 用户人品预测大赛--getmax队--竞赛分享
  2. OSSIM系统用户审计
  3. FTP软件VSFTP配置文件详解
  4. 长城电脑或收购夏新电子笔记本业务
  5. LeetCode-166- Fraction to Recurring Decimal
  6. MySQL 中 6 个常见的日志问题
  7. 在VS2005中 GridView导入Excel的两点小技巧-附源码
  8. JDK7 源码学习系列——ThreadLocal
  9. eclipse总跳出password required的框解决办法
  10. oracle数据源的报表sql计算慢解决