等待队列和竞争条件

Wait queue and race condition

我正在阅读 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);

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

是的,这段代码实际上是 raced,在 prepare_to_waitschedule 调用之间它应该是对 condition 的检查(并且满意就破)。

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

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);
...

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

This function follows the pattern laid out in our example. The main difference is that it checks for the condition in the body of the while() loop, instead of in the while() statement itself. This is because checking the condition is complicated and requires grabbing locks. The loop is terminated via break.

但是,该代码展示了另一种模式,它检查prepare_to_waitschedule 调用之间的条件。该代码实际上 正确 (无竞争)。 此外,该代码不使用 add_wait_queue,这在存在 prepare_to_wait.

的情况下是多余的

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