In Linux, a wait queue is managed by means of a “wait queue head,” a
structure of typewait_queue_head_t
, which is defined in
. A wait queue head can be defined and initialized
statically with:
在Linux中等待队列是由一个等待队列头管理的。
DECLARE_WAIT_QUEUE_HEAD(name);
or dynamicly as follows:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
When a process sleeps, it does so in expectation that some condition
will become true in the future. As we noted before, any process that
sleeps must check to be sure that the condition it was waiting for is
really true when it wakes up again. The simplest way of sleeping in
the Linux kernel is a macro calledwait_event
(with a few
variants); it combines handling the details of sleeping with a check
on the conditiona process is waiting for. The forms of wait_event
are:
当一个进程睡眠时,它还是会期望一些条件在未来会改变。
当一个睡眠的进程被再次唤醒时,它必须先检查一下条件以确保它等待的条件是否真的发生了。
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
In all of the above forms,
queue is the wait queue head to use. Notice that it is passed “by value.” The condition is an arbitrary boolean expression that is
evaluated by the macro before and after sleeping; until condition
evaluates to a true value, the process continues to sleep.Note that condition may be evaluated an arbitrary number of times, so
it should not have any side effects.The preferred alternative is
wait_event_interruptible
which can be
interrupted by signals. This version returns an integer value that you
should check; a nonzero value means your sleep was interrupted by some
sort of signal, and your driver should probably return -ERESTARTSYS.
wait_event_interruptible 可以被一个信号中断。
该函数会返回一个整型,你必须去检查一下,非零值意味着睡眠被一些信号打断了。
当你调用该函数时,它会去检查一下当前的执行条件是否为真,当为真时,该进程不会因为等待运行条件而被挂起,会继续执行。
如果此时添加为假,其会睡眠。
成功地唤醒一个被wait_event_interruptible()的进程,需要满足:
1)condition为真的前提下
2)调用wake_up()。
**原因:
1. 一个进程睡眠时,本进程将自动再次被设
置为TASK_INTERRUPTIBLE状态;该进程会被从运行队列中移除,移动到到之前申明等待队列中,其再不会被运行;且这段代码会被放到一个由condition 控制的
for(;;)
{
if(condition=true)
break;
}
(类似的)循环中,此时该循环并没有执行 。
2. 而wake_up的作用就是将一个等待队列中的进程放到执行队列中,这时,该进程会重新运行,而其被condition的循环阻塞,所以这个运行起来的进程还会去检查一下condition,为真则跳出死循环,继续执行该进程。
3. 如果wake_up时条件condition不满足,本进程将自动再次被设
置为TASK_INTERRUPTIBLE状态,接下来执行schedule()的结果是再次被
从runqueue队列中删除
**
The final versions (wait_event_timeout and
wait_event_interruptible_timeout) wait for a limited time; after that
time period (expressed in jiffies) expires, the macros return with a
value of 0 regardless of how condition evaluates.The other half of the picture, of course, is waking up. Some other
thread of execution (a different process, or an ***interrupt
handler***, perhaps) has to perform the wakeup for you, since your
process is, of course, asleep. The basic function that wakes up
sleeping processes is called wake_up. It comes in several forms (but
we look at only two of them now):
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
wake_up wakes up all processes waiting on the given queue (though the
situation is a little more complicated than that, as we will see
later). The other form (wake_up_interruptible) restricts itself
to processes performing an interruptible sleep. In general, the two
are indistinguishable (if you are using interruptible sleeps); in
practice, the convention is to usewake_up
if you are using
wait_event
andwake_up_interruptible
if you use
wait_event_interruptible
.
原型
/**
* wait_event_interruptible - sleep until a condition gets true
* @wq: the waitqueue to wait on
* @condition: a C expression for the event to wait for
*
* The process is put to sleep (TASK_INTERRUPTIBLE) until the
* @condition evaluates to true or a signal is received.
* The @condition is checked each time the waitqueue @wq is woken up.
* ***该进程会被条件或者信号唤醒。条件的检查会在每次wake_up队列时;***
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.
*
* The function will return -ERESTARTSYS if it was interrupted by a
* signal and 0 if @condition evaluated to true.
* 被信号量和条件唤醒会返回不同的值
*/
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition))
/***当你调用该函数时,它会去检查一下当前的执行条件是否为真,当为真时,该进程不会因为等待运行条件而被挂起,会继续执行。如果此时添加为假,其会睡眠。***/ \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
#define __wait_event_interruptible_timeout(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); /*系统自己定义一个等待*/ \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { /***会被一个信号悬挂起来,有信号来时也会跳出该语句,继续执行。***/ \
ret = schedule_timeout(ret); \
if (!ret) \
break; \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
finish_wait(&wq, &__wait); /*结束睡眠,从系统自定义的等待中退出,放到我们自己定义的等待队列头*/ \
} while (0)
例子
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) going to sleep\n",
current->pid, current->comm);
**wait_event_interruptible(wq, flag != 0);**
flag = 0;
printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
return 0; /* EOF */
}
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count,
loff_t *pos)
{
printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
current->pid, current->comm);
flag = 1;
**wake_up_interruptible(&wq);**
return count; /* succeed, to avoid retrial */
}
Note the use of the flag variable in this example. Since wait_event_interruptible checks for a condition that must become true, we use flag to create that condition.