Date: Fri, 19 Apr 2002 19:18:24 +0000 (UTC)
From: Valentin Nechayev <netch@segfault.kiev.ua>
Newsgroups: fido7.ru.unix.prog
Subject: Блокировка процессов в Linux
> Кстати, функцией interruptible_sleep_on() пользоваться не рекомендуют -
> в большинстве случаев создается race condition. Т.е. обычная картина -
> проверили условие (например, пустоту буфера), вызвали
> interruptible_sleep_on() - но между проверкой и переходом в состояние
> ожидания может произойти прерывание, которое положит данные в этот
> буфер, а процесс этого не заметит и останется ждать следующего. В
> <linux/sched.h> есть макрос wait_interruptible(wq, condition), который
> позволяет избежать этой ситуации (хитрость в том, что нужно проверять
> условие _после_ постановки процесса в очередь). В 2.2.16 он точно есть
> (в первых 2.2.x еще не было); в 2.4.x - тем более.
Макрос этот называется wait_event[_interruptible].
По крайней мере, в 2.4.18.
Но он годится только для крайне простых условий, которые можно проверить
атомарно. Для сложных порядок будет куда более заковыристый:
lock_subsystem;
if( need_wait ) {
add_to_waitqueue;
set_current_state( TASK_INTERRUPTIBLE );
if(signal_pending(current)) { ... }
unlock_subsystem;
/* с этого момента могут приходить посторонние воздействия.
* если они придут, state изменится на TASK_RUNNING, и schedule()
* вернет управление немедленно, без задержки
*/
schedule(); /* может быть, schedule_timeout() */
lock_subsystem;
}
unlock_subsystem;
Предполагается в качестве обязательного условия, что тот, кто делает
wakeup, тоже сделает lock_subsystem, и что schedule() и wake_up*()
используют общий лок на очереди шедулера.
Я бы все это безобразие с кишками наружу - оформил в виде функции
с callback'ами.
> Кстати, на http://www.oreilly.com/catalog/linuxdrive2/ дают книжку (на
> английском) по написанию драйверов для Linux.
/netch
From: Valentin Nechayev <netch@segfault.kiev.ua>
SV> при инициализации надо сделать
SV> init_waitqueue(&xx_wait);
> Hе поверишь, после того как я эту функцию вызвал на sleep_on всё равно
> валится
Что-то еще не доделал. Сравни код.
> Пробовал (видимо имеется ввиду wait_event_interruptible) работает, но
> только после того как я в этот макрос поправку внёс, там была такая строка:
> current->task = TASK_INTERRUPTIBLE;
> Я заменил её на:
> current->task = TASK_RUNNING;
> иначе он доходил до функции schedule и ждал чего-то (не вис, а ждал) я
> посмотрел хелп там написано что schedule не возвратит управление пока
> процесс не продолжится выполняться.
Ась?
TASK_RUNNING тебе должен дать только один эффект (разумеется, если
доблестные вояки не переделали это хозяйство) - управление немедленно
вернется обратно, или же отдаст управление кому-то более приоритетному.
И собственно все. Ты сделал так, что драйвер жрет весь процессор,
пока не получит результат. Что ж, это тоже в чем-то метод - очень быстрый
и очень грязный хак, если нет 10 минут на нормальный. Но это нехорошо.
Ты, по-моему, не учитываешь одного существенного момента.
Когда впадаешь в простой sleep, надо четко определить:
1. На каком объекте спать. Объект задается просто адресом в адресном
пространстве ядра. Смысл его ровно то, что кто-то по нему сделает wakeup.
Если wakeup не сделают и ты не задал таймаут для сна, и не пришел сигнал -
то так и будешь спать до скончания века.
2. Кто, когда, почему сделает wakeup. Если никто не делает, или делает
на другой адрес - будешь опять же спать до посинения.
3. Сделать, чтобы wakeup вызывался и притом на тот же адрес.
В add_wait_queue() первым параметром идет адрес для сна. Вот пример: