日期:2014-05-16  浏览次数:20810 次

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)创建和初始化一个等待队列。常由宏