Кажется, что каждое устройство Linux реализовано немного по-разному, и предпочтительный способ, по-видимому, меняется каждые несколько выпусков Linux по мере добавления более безопасных / более быстрых функций ядра, но обычно:
- Драйвер устройства создает чтение и
записывать очереди ожидания для устройства.
- Любой поток процесса, желающий ждать
для ввода / вывода ставится на соответствующий
очередь ожидания Когда происходит прерывание
обработчик просыпается один или несколько
ожидающие темы. (Очевидно,
потоки не запускаются сразу, поскольку мы прерываем
контекст, но добавляются к
очередь планирования ядра).
- Когда запланировано ядром
поток проверяет, если условия
правы, чтобы продолжить - если нет
он возвращается в очередь ожидания.
Типичный пример (слегка упрощенный):
В драйвере при инициализации:
init_waitqueue_head(&readers_wait_q);
В функции чтения драйвера:
if (filp->f_flags & O_NONBLOCK)
{
return -EAGAIN;
}
if (wait_event_interruptible(&readers_wait_q, read_avail != 0))
{
/* signal interrupted the wait, return */
return -ERESTARTSYS;
}
to_copy = min(user_max_read, read_avail);
copy_to_user(user_buf, read_ptr, to_copy);
Тогда обработчик прерываний просто выдает:
wake_up_interruptible(&readers_wait_q);
Обратите внимание, что wait_event_interruptible () - это макрос, который скрывает цикл, проверяющий условие - в данном случае read_avail != 0
, и многократно добавляет его в очередь ожидания, если он проснулся, когда условие не выполнено.
Как уже упоминалось, существует ряд вариаций, основной из которых является то, что если потенциально обработчику прерываний потенциально предстоит проделать большую работу, он сам выполняет минимальный минимум и откладывает остальное в рабочую очередь или тасклет (обычно известная как «нижняя половина»), и именно она разбудит ожидающие потоки.
См. Книгу драйверов устройств Linux для получения дополнительной информации - pdf доступен здесь:
http://lwn.net/Kernel/LDD3