【内核】:进程休眠的方法

转载 2011年10月12日 23:36:37
 休眠的步骤
将一个进程置于休眠状态,一般步骤如下:
0. 定义并初始化(如果还没有的话)一个等待队列头(wait_queue_head_t),这个等待队列头应该是能被要休眠的进程和负责唤醒的进程都能访问到。
1. 对进程的每次休眠,定义并初始化一个等待队列(wait_queue_t)
2. 把等待队列加入到相应的等待队列头中。
3. 把进程状态置为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE
4. 再次检查休眠条件是否为真,否则跳过第5步
5. 执行 schedule()
6. 清理:将进程状态改为 TASK_RUNNING(通常已经是,除非是从第4步跳过来的),把等待队列从等待队列头中删除(防止多次唤醒)
7. 如果是可中断休眠的话(TASK_INTERRUPTIBLE),检查是否是因信号而被唤醒。如果是的话,一般直接 return -ERESTARTSYS 让上层处理。
8. 检查需要等待的条件是否满足,如果不是的话,再回到第1步重新开始。

注意,第4步的检查条件非常重要。因为到第3步之前,条件可能已被满足,而如果我们再把进程状态设为 TASK_INTERRUPTIBLE 或 TASK_UNINTERRUPTIBLE ,直接执行 schedule() 的话就可能再也无法被调度到。但如果条件是在第4步到第5步之间满足的话,那不用担心,因为既然条件能满足,就应该有唤醒,而我们的进程状态应该又被设为了 TASK_RUNNING,所以 schedule() 会调度到我们。

另外,以上只是一个一般的步骤,具体休眠时可根据个人的理解和实际的需要灵活调整。

内核提供的操作方法
根据上面的步骤,内核提供了至少两种方法来帮助实现,我把他们称作是半自动DIY式的和全自动傻瓜式。

半自动DIY式
wait_queue_head_t wq; init_waitqueue_head(&wq);        --对应第0步
DEFINE_WAIT(wait);                                     --对应第1步
prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);       --对应第2,3步
if (condition) schedule();                             --对应第4,5步
finish_wait(&wq, &wait);                               --对应第6步
if (signal_pending(current)) return -ERESTARTSYS;      --对应第7步


全自动傻瓜式
wait_queue_head_t wq; init_waitqueue_head(&wq);        --对应第0步
int ret = wait_event_interruptible(wq, condition);     --对应第1,2,3,4,5,6步和第7步前半步
if (ret != 0) return -ERESTARTSYS;                     --对应第7步后半步


如果看内核代码的话,就可以发现wait_event系列其实就是对第1,2,3,4,5,6,7步的一个宏包装

唤醒
唤醒的操作相对简单,就用 wake_up 系列函数。比如 wake_up_interruptible(&wq),它会把等待队列头(wq)上的所有进程都去唤醒。

实现细节
关于休眠的内核实现细节,其实半自动DIY式的每一步所做的事都对应相应的休眠步骤中所描述的,只有第1步中初始化一个等待队列(wait_queue_t)的概念比较模糊。

先看一下一个等待队列(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;


再看一下 DEFINE_WAIT 的宏定义:
#define DEFINE_WAIT(name)                        /
    wait_queue_t name = {                        /
        .private    = current,                   /
        .func        = autoremove_wake_function,           /
        .task_list    = LIST_HEAD_INIT((name).task_list),  /
    }


由此可见,private字段被指向了当前进程,func字段是一个函数指针,当内核要唤醒一个等待队列时,内核就会调用 func 所指向的函数来唤醒进程,而 autoremove_wake_function() 就是内核提供的唤醒等待队列中进程的方法,它其实主要做两件事:1. 调用 default_wake_function() 来唤醒进程;2. 把等待队列从等待队列头中删除。

关于唤醒,内核最主要会调用到 __wake_up_common() 这个函数,下面是它的代码实现:
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode, int nr_exclusive, int sync, 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, sync, key) && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
            break;
    }
}


其他变种
关于休眠和唤醒,内核还提供了一些其他变种的方法,这里就不一一说明了,下面只列出了一些常用的宏和函数名称。
wait_event(queue, condition);
wait_event_interruptible(queue, condition);
wait_event_timeout(queue, condition, timeout);
wait_event_interruptible_timeout(queue, condition, timeout);
void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)

wake_up(queue)
wake_up_nr(queue, nr)
wake_up_interruptible(queue)
wake_up_interruptible_nr(queue, nr)


休眠规则

不要在原子上下文中休眠。
禁止中断时,也不能休眠。
要确保有进程能唤醒自己。
休眠被唤醒之后仍要检查等待的条件是否为真,否则重新继续休眠。

python线程进程、异步IO

多线程是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间 执行多于一个线程,进而提升整体处理性能。 该章节主要包括线程进程的概念,基于python的线程进程实现,GIL锁的影响,消费者生产者模型,进程池的应用以及IO模型的介绍,一句话概括本章的内容就是:实现并发编程,即计算机能够同时处理多个任务。
  • 2017年03月06日 18:23

Linux kernel 进程的睡眠和唤醒

Linux进程的睡眠和唤醒 1 Linux进程的睡眠和唤醒 在Linux中,仅等待CPU时间的进程称为就绪进程,它们被放置在一个运行队列中,一个就绪进程的状 态...
  • stephen_yin
  • stephen_yin
  • 2011-09-13 17:06:27
  • 2498

linux内核之休眠

dasdas
  • u013256018
  • u013256018
  • 2015-09-10 11:35:52
  • 1105

Linux进程休眠和唤醒

当进程以阻塞的方式通信,在得到结果前进程会挂起休眠。 为了将进程以一种安全的方式进入休眠,我们需要牢记两条规则: 一、永远不要在原子上下文中进入休眠。 二、进程休眠后,对环境一无所知。唤醒后,必...
  • u011068464
  • u011068464
  • 2013-08-27 23:35:07
  • 1203

linux内核休眠与唤醒

转自:http://blog.csdn.net/zhandoushi1982/article/details/5759050       在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态...
  • njuitjf
  • njuitjf
  • 2014-01-08 11:36:22
  • 2012

linux内核休眠与唤醒 && 安卓系统控制内核的状态

在Linux中,休眠主要分三个主要的步骤:(1)冻结用户态进程和内核态任务;(2)调用注册的设备的suspend的回调函数;(3)按照注册顺序休眠核心设备和使CPU进入休眠态。      冻结进程是内...
  • zhandoushi1982
  • zhandoushi1982
  • 2010-07-23 23:59:00
  • 5705

Linux进程的睡眠和唤醒(一个定时信号唤醒睡眠中的进程)

突然想到Nginx中时间更新这块处理,Nginx中为了减少调用系统调用gettimeofday这个函数(因为一旦调用了系统调用,就会使得进程从用户态切换到内核态,就会发生上下文切换,这个代价很大且不值...
  • yusiguyuan
  • yusiguyuan
  • 2015-08-20 11:35:56
  • 5343

c#休眠

System.Threading.Thread.Sleep(1000); //休眠一秒
  • wei_jie_zhang
  • wei_jie_zhang
  • 2015-07-16 16:47:03
  • 1519

Linux 内核睡眠的几种方式

有2-3个不同的睡在Linux内核的方法。 睡眠的第一和简单的方法是当前进程的状态设置为可中断或NON_INTERRUPTIBLE然后调用时间表。 的状态设置为除运行其他很重要,因为只有这样,内核将...
  • robertsong2004
  • robertsong2004
  • 2014-08-18 14:07:16
  • 3865

关于进程的休眠和唤醒(转)

转自:http://bbs.java.ccidnet.com/read.php?tid=655240LKD中的讲解休眠(被阻塞)的进程处于一个特殊的不可执行状态。这点非常重要,否则,没有这种特殊状态的...
  • loyal_baby
  • loyal_baby
  • 2009-05-17 22:33:00
  • 3762
收藏助手
不良信息举报
您举报文章:【内核】:进程休眠的方法
举报原因:
原因补充:

(最多只允许输入30个字)