Очередь ожидания и состояние гонки - PullRequest
0 голосов
/ 14 июля 2020

Я читаю «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);

Мой вопрос такой же, как в коде выше. Что произойдет, если условие будет изменено и wake_up() будет вызван после проверки условия, но до prepare_to_wait? Моя (вероятно, неправильная) интерпретация заключается в том, что поскольку prepare_to_wait делает поток TASK_INTERRUPTIBLE и вызывает schedule() после изменения условия, он спит вечно (если только он не получит сигнал или не будет вызван другой wake_up).

1 Ответ

2 голосов
/ 14 июля 2020

Да, этот код на самом деле гонка , между вызовами prepare_to_wait и schedule это должна быть проверка на condition (и взлом, если он удовлетворен).

Довольно забавно, что в следующем описании книга ссылается (на странице 60) на реализацию функции inotify_read() в 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);
...

, которая, по словам автора, «следует шаблону»:

Эта функция следует шаблону, представленному в нашем примере. Основное отличие состоит в том, что он проверяет условие в теле оператора while() l oop, а не в самом выражении while(). Это связано с тем, что проверка условия сложна и требует захватывающих блокировок. L oop завершается через break.

Однако этот код демонстрирует другой шаблон , который проверяет условие между вызовами prepare_to_wait и schedule. И этот код на самом деле правильный (без гонок). Также этот код не использует add_wait_queue, что является лишним при наличии prepare_to_wait.

В другой книге того же автора, Linux Разработка драйверов (3-я версия), использование очередей ожидания кажется если быть более точным. См., Например, главу 6, Расширенные операции с драйверами Char.

...