当你在用户空间需要读写一大片数据的时候,这个就用上了。
以下来自:http://www.yuanma.org/data/2006/1207/article_1916.htm
interruptible_sleep_on( &wq ) 是用来将目前的 process,也就是要求写资料到buffer 的 process放到 wq 这个 wait_queue 里。在 interruptible_sleep_on 里,则是最后会呼叫 schedule() 来做 schedule 的动作,谁调用了schedule谁就趴下,让别人去运行,醒来就原地起来,执行schedule()后的代码。那那个调用了schedule的家伙什么 醒过来呢?这时候就需要用到另一个函数了wake_up_interruptible()了。
以下来自:http://tauruspdj.blog.163.com/blog/static/4312500620090794030998/
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 成对使用。
【补充】其实看了那么多,他们也没有给个立即可用的步骤,写blog嘛,就是分享心得。我基于2.6.24总结一下,希望对大家有帮助:
1、定义:wait_queue_head_t my_queue;
2、初始化 init_waitqueue_head(&my_queue);
3、在一个函数里面等待:wait_event(queue, condition) ;(别在中断里面搞)
4、在另一个函数里面唤醒:wake_up(wait_queue_head_t *queue); (这个可以在中断调用,去唤醒别的进程,特别是dma操作类的)
有好几个等待和唤醒函数,大家可以慢慢试。
add_wait_queue() 用来将一个进程添加到等待队列,该函数在获得必要的自旋锁后,使用 __add_wait_queue() 函数来完成队列添加工作。
__add_wait_queue() 定义为:
引用
1234static
inline
void
__add_wait_queue(wait_queue_head_t *head, wait_queue_t *
new
)
{
list_add(&
new
->task_list, &head->task_list);
}
list_add() 是标准的建立队列双向链表函数。
另外还有一个 add_wait_queue_exclusive() 函数,它的工作方式和 add_wait_queue() 一样,但是将进程插入到队列尾部,同时还设置了 WQ_EXCLUSIVE 标志。
add_wait_queue_exclusive() 定义为:
引用
12345678910void
fastcall add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned
long
flags;
wait->flags |= WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock, flags);
__add_wait_queue_tail(q, wait);
spin_unlock_irqrestore(&q->lock, flags);
}
__add_wait_queue_tail() 函数定义为:
引用
12345static
inline
void
__add_wait_queue_tail(wait_queue_head_t *head,
wait_queue_t *
new
)
{
list_add_tail(&
new
->task_list, &head->task_list);
}
list_add_tail() 定义为:
引用
123456789101112/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static
inline
void
list_add_tail(
struct
list_head *
new
,
struct
list_head *head)
{
__list_add(
new
, head->prev, head);
}
由此可见,添加到队列尾也是调用 __list_add() 函数,只是第 2 个参数和第 3 个参数与原来的增加到队列头的参数调换了一下顺序。
关于建立队列双向链表过程可参考:http://www.groad.net/bbs/read.php?tid-3181.html
#define DECLARE_WAITQUEUE (name, tsk) /
wait_queue_t name =__WAITQUEUE_INITIALIZER(name, tsk)
#define __WAITQUEUE_INITIALIZER(name, tsk) { task: tsk, task_list: { NULL, NULL }, __WAITQUEUE_DEBUG_INI(name)}
它的解释是:
通过DECLARE_WAITQUEUE宏将等待队列项初始化成对应的任务结构,并且用于连接的相关指针均设置为空。其中加入了调试相关代码。
进程通过执行下面步骤将自己加入到一个等待队列中:
1) 调用DECLARE_WAITQUEUE()创建一个等待队列的项;
2) 调用add_wait_queue()把自己加入到等待队列中。该队列会在进程等待的条件满足时唤醒它。在其他地方写相关代码,在事件发生时,对等的队列执行wake_up()操作。
3) 将进程状态变更为: TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE。
4) 如果状态被置为TASK_INTERRUPTIBLE ,则信号唤醒进程。即为伪唤醒(唤醒不是因为事件的发生),因此检查并处理信号。
5) 检查condition是否为真,为真则没必要休眠,如果不为真,则调用scheduled()。
6) 当进程被唤醒的时候,它会再次检查条件是否为真。真就退出循环,否则再次调用scheduled()并一直重复这步操作。
7) condition满足后,进程将自己设置为TASK_RUNNING 并通过remove_wait_queue()退出。