等待队列和竞争条件

Shu*_*uki 2 linux-kernel

我正在阅读 Robert Love 的《Linux 内核开发》,并找到下面的代码来等待事件。

DEFINE_WAIT(wait);

add_wait_queue(q, &wait); 
while (!condition) {
    // What happens if condition is changed and wake_up() is called here ?
    prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE); 
    if (signal_pending(current))
        /* handle signal */ 

    schedule();
}

finish_wait(&q, &wait);
Run Code Online (Sandbox Code Playgroud)

我的问题如上面的代码所示。如果条件更改并wake_up()在条件检查之后但之前调用,会发生什么情况prepare_to_wait?我在这里的(可能是错误的)解释是,因为在条件改变后prepare_to_wait创建线程TASK_INTERRUPTIBLE并调用,所以它永远休眠(除非它收到信号或调用另一个线程)。schedule()wake_up

Tsy*_*rev 5

是的,这段代码实际上是在和 之间进行了竞争,调用它应该是检查(如果满足则中断)。prepare_to_waitschedulecondition

inotify_read()有趣的是,在以下描述中,本书提到(第 60 页)文件中函数的实现fs/notify/inotify/inotify_user.c

DEFINE_WAIT(wait);
...
while (1) {
  prepare_to_wait(&group->notification_waitq,
    &wait,
    TASK_INTERRUPTIBLE);

  if (<condition>) // very simplified form of checks
    break;
  if (signal_pending(current))
    break;

  schedule();
}
finish_wait(&group->notification_waitq, &wait);
...
Run Code Online (Sandbox Code Playgroud)

根据作者的说法,它“遵循模式”:

该函数遵循我们示例中列出的模式。主要区别在于它检查循环体中的条件while(),而不是while() 语句本身。这是因为检查条件很复杂并且需要抢锁。循环通过 终止break

然而,该代码展示了另一种模式prepare_to_wait,它检查和调用之间的条件schedule。该代码实际上是正确的(无竞争)。此外,该代码不使用add_wait_queue,这在存在 的情况下是多余的prepare_to_wait

在同一作者的另一本书《Linux Driver Development (3d revision)》中,等待队列的用法似乎更准确。例如,请参见第 6 章“高级字符驱动程序操作”。