等待队列——休眠与唤醒

原创 2012年03月29日 20:58:29

内核中的休眠是通过等待队列来处理的。等待队列是一个由正在等待某个事件发生的进程组成的一个简单链表。在内核用wait_queue_head_t来表示。

定义:

DECLARE_WAITQUEUE()(静态定义)

init_waitqueue_head()(动态定义)

在内核中实现休眠的方法有点复杂,实现的模板如下:


/*qisthewaitqueuewewishtosleepon*/

DEFINE_WAIT(wait);

add_wait_queue(q,&wait);//这个函数调用是可选

while(!condition){/*conditionistheeventthatwearewaitingfor*/

prepare_to_wait(&q,&wait,TASK_INTERRUPTIBLE);

if(signal_pending(current))

/*handlesignal*/

schedule();

}

finish_wait(&q,&wait);

一个进程执行如下步骤将自己加入到一个等待队列中:

  • 通过宏DEFINE_WAIT()来创建一个等待队列项。

  • 通过函数add_wait_queue()将该项加入到一个等待队列中。当等待的事件(条件)为真时,等待队列会唤醒该进程项。当然,需要在其他地方调用wake_up()函数。

  • 调用prepare_to_wait()函数将进程的状态改为TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE。该函数也会在必要的时候将进程加回到等待队列中,在后续的迭代中会用到(提示:第二个步骤可选,因为该函数在任务列表为空的时候也会将当前任务项加入到等待队列中)。

  • 如果状态设置为TASK_INTERRUPTIBLE,,一个信号会唤醒该进程。这称为伪休眠。因此要检测和处理信号。

  • 当进程被唤醒,它再次检测条件是否为真。如果为真,它会退出循环。否则,再次调用schedule()然后重复上述过程。

  • 当条件为真,该进程会将其状态设为TASK_RUNNING并将自己通过finish_wait()从等待队列中删除。

一个例子:

staticssize_t inotify_read(struct file *file, char __user *buf,

size_t count, loff_t *pos)

{

structfsnotify_group *group;

structfsnotify_event *kevent;

char__user *start;

intret;

DEFINE_WAIT(wait);


start= buf;

group= file->private_data;


while(1) {

prepare_to_wait(&group->notification_waitq,&wait,TASK_INTERRUPTIBLE);


mutex_lock(&group->notification_mutex);

kevent= get_one_event(group, count);

mutex_unlock(&group->notification_mutex);


if(kevent) {

ret= PTR_ERR(kevent);

if(IS_ERR(kevent))

break;

ret= copy_event_to_user(group, kevent, buf);

fsnotify_put_event(kevent);

if(ret < 0)

break;

buf+= ret;

count-= ret;

continue;

}


ret= -EAGAIN;

if(file->f_flags & O_NONBLOCK)

break;

ret= -EINTR;

if(signal_pending(current))

break;


if(start != buf)

break;


schedule();

}


finish_wait(&group->notification_waitq,&wait);

if(start != buf && ret != -EFAULT)

ret= buf - start;

returnret;

}

另一种模板:

/*Helper thread */

staticint

my_thread(void*unused)

{

/**

  *                wait_queue_t wait;

  *                init_waitqueue_entry(&wait, current);

  */

DECLARE_WAITQUEUE(wait,current);


daemonize("my_thread");

add_wait_queue(&my_thread_wait,&wait);


while(1) {

/*Relinquish processor until event occurs */

set_current_state(TASK_INTERRUPTIBLE);

if(signal_pending(current))

/*##handlesingalevent##*/

schedule();

/*Control gets here when the thread is woken

upfrom the my_thread_wait wait queue */


/*Quit if let go */

if(pink_slip) {

break;

}

/*Do the real work */

/*... */


}


/*Bail out of the wait queue */

__set_current_state(TASK_RUNNING);

remove_wait_queue(&my_thread_wait,&wait);


/*Atomically signal completion and exit */

complete_and_exit(&my_thread_exit,0);

}

唤醒

通过函数wake_up()唤醒,它将唤醒所有在特定等待队列上等待的进程。一般情况下默认的唤醒函数为:default_wake_function()。它会调用try_to_wake_up(),将被唤醒的进程状态设置为TASK_RUNNING,然后调用enqueue_task()将该进程加入到红黑树中,如果被唤醒的进程的优先级大于当前进程的优先级,设置need_resched1。休眠与唤醒之间的关系如下:

\

伪唤醒是指进程是因为接收到某个信号而被唤醒,而不是等待事件发生而导致其被唤醒。


在最新的内核代码中,一般会使用更高层的接口:wait_eventwait_event_timeout接口。使用wake_up_all唤醒所有添加到某个等待队列链表中的等待队列。使用模板如下:


1. 初始化一个等待队列头:

 

  init_waitqueue_head(&ret->wait_queue);

 

  注: 判断队列是否为空: waitqueue_active(...), 返回false即表示队列为空.

 

2. 等待某个条件发生:

 

   wait_event(...) 或 wait_event_timeout(...)

 

3. 唤醒队列

 

   wake_up_all(...)

 



Java并发编程(二)线程同步和等待唤醒机制

概述线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。 因此为了解决这个问题,我们可能需要这样一...
  • huaxun66
  • huaxun66
  • 2017年09月12日 10:38
  • 628

java中线程队列BlockingQueue的用法

认识BlockingQueue阻塞队列,顾名思义,首先它是一个队列,而一个队列在数据结构中所起的作用大致如下图所示: 从上图我们可以很清楚看到,通过一个共享的队列,可以使得数据由队列的一端输入,从另外...
  • landebug
  • landebug
  • 2015年07月30日 12:29
  • 2009

linux poll 和 等待队列休眠的关系(基于kernel 3.10.0)

原理分析来自网上,代码由本人梳理分析阅读vhost的时候,发现使用了大量的等待队列和poll,这里温故而知新一下。注:wait_queue_t是等待在wait_queue_head_t队列中的等待元素...
  • leoufung
  • leoufung
  • 2015年09月28日 09:56
  • 582

linux驱动之等待队列

等待队列工作过程就是睡眠和唤醒的过程 使用: static DECLARE_WAIT_QUEUE_HEAD(button_waitq);     //定义初始化等待队列头 wake_up_i...
  • u012590688
  • u012590688
  • 2015年05月28日 12:12
  • 591

等待/通知机制:

等待和通知在线程中的使用方法和使用示例
  • qq455276333
  • qq455276333
  • 2016年09月03日 17:51
  • 1525

Java多线程中wait, notify and notifyAll的使用

在Java的Object类中有三个final的方法允许线程之间进行资源对象锁的通信,他们分别是: wait(), notify() and notifyAll()。 调用这些方法的当前线程必须拥有此...
  • luoweifu
  • luoweifu
  • 2015年06月27日 22:36
  • 14877

Android的休眠与唤醒 && 实例

转自:http://blog.csdn.net/zhandoushi1982/article/details/5765219         Android 休眠(suspend),在一个打过an...
  • njuitjf
  • njuitjf
  • 2014年01月08日 10:57
  • 1102

android的休眠和唤醒流程

android休眠唤醒流程: power按键事件上报给android系统,最终由windownmanager接收到,当有按键事件时判断是否需要休眠后唤醒系统,然后调用powermanager系统服务...
  • Wilsonboliu
  • Wilsonboliu
  • 2014年10月23日 19:06
  • 3500

java交互:通知和等待

一、线程交互的基础知识(通知和等待) a)        void notify() 唤醒在此对象监视器上等待的单个线程 b)        void notifyAll() 唤醒在此对象监视器上等待...
  • u011159417
  • u011159417
  • 2016年08月24日 15:51
  • 481

并发 加锁 以及Java api自身的阻塞队列

Java里的阻塞队列 阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会...
  • zhuangyalei
  • zhuangyalei
  • 2015年09月21日 16:50
  • 1955
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:等待队列——休眠与唤醒
举报原因:
原因补充:

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