我试图了解在linux内核中如何实现wait_event。 ldd3中有一个代码示例,其中使用prepare_to_wait(http://www.makelinux.net/ldd3/chp-6-sect-2)解释内部实现。
static int scull_getwritespace(struct scull_pipe *dev, struct file *filp)
{
while (spacefree(dev) == 0) {
DEFINE_WAIT(wait);
up(&dev->sem);
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
PDEBUG("\"%s\" writing: going to sleep\n",current->comm);
prepare_to_wait(&dev->outq, &wait, TASK_INTERRUPTIBLE);
if (spacefree(dev) == 0) // Why is this check necessary ??
schedule( );
finish_wait(&dev->outq, &wait);
if (signal_pending(current))
return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
}
return 0;
}
在书中,解释如下。
然后是对缓冲区的强制检查;我们必须处理此案 我们输入后缓冲区中的空间可用 while循环(并删除信号量)但在我们自己之前 进入等待队列。没有那个检查,如果读者进程是 能够在那个时间完全清空缓冲区,我们可能会错过 只有醒来,我们永远都会得到并永远沉睡。满意了 我们必须睡觉,我们可以打电话给我们。
我无法理解这条解释。如果在调用schedule()之前未完成if (spacefree(dev) == 0)
,我们将如何进入无限期睡眠状态?
如果此强制性检查不存在,则wakeup()仍会将进程状态重置为TASK_RUNNING并按照下一段中的说明安排返回。
值得再看一下这种情况:如果唤醒会发生什么 发生在if语句中的测试和调度调用之间? 在那种情况下,一切都很好。唤醒将进程状态重置为 TASK_RUNNING和安排返回 - 虽然不一定马上。 只要测试发生在该过程已经完成之后 等待队列并改变其状态,事情就会奏效。
答案 0 :(得分:3)
重要的是,在调用 prepare_to_wait()
之后完成了(最后一次)检查。
prepare_to_wait()
将指向当前进程的指针放入等待队列。如果在<{em> prepare_to_wait()
调用之前发生唤醒,则唤醒将无法影响当前进程。