Внутреннее устройство ядра Linux 2.4


Очереди ожидания (Wait Queues) - часть 2


ssize_t rtc_read(struct file file, char *buf, size_t count, loff_t *ppos) { DECLARE_WAITQUEUE(wait, current); unsigned long data; ssize_t retval;

add_wait_queue(&rtc_wait, &wait); current->state = TASK_INTERRUPTIBLE; do { spin_lock_irq(&rtc_lock); data = rtc_irq_data; rtc_irq_data = 0; spin_unlock_irq(&rtc_lock);

if (data != 0) break;

if (file->f_flags & O_NONBLOCK) { retval = -EAGAIN; goto out; } if (signal_pending(current)) { retval = -ERESTARTSYS; goto out; } schedule(); } while(1); retval = put_user(data, (unsigned long *)buf); if (!retval) retval = sizeof(unsigned long);

out: current->state = TASK_RUNNING; remove_wait_queue(&rtc_wait, &wait); return retval; }

Разберем функцию rtc_read():

  1. Объявляется новый элемент очереди ожидания указывающий на текщий процесс.
  2. Этот элемент добавляется в очередь rtc_wait.
  3. Текущий процесс переводится в состояние TASK_INTERRUPTIBLE которое предполагает, что процесс не должен учавствовать в процессе планирования.
  4. Проверяется - доступны ли данные. Если да - то цикл прерывается, данные копируются в пользовательский буфер, процесс переводится в состояние TASK_RUNNING, удаляется из очереди и производится возврат.
  5. Если данные недоступны, а пользователь запросил неблокирующую опрацию ввода-вывода, то возвращается код ошибки EAGAIN (который имеет тоже значение, что и EWOULDBLOCK)
  6. При наличии ожидающих обработки сигналов - "верхнему уровню" сообщается, что системный вызов должен быть перезапущен, если это необходимо. Под "если это необходимо" подразумеваются детали размещения сигнала, как это определено в системном вызове sigaction(2)
  7. Далее задача "отключается", т.е. "засыпает", до "пробуждения" обработчиком прерывания. Если не переводить процесс в состояние TASK_INTERRUPTIBLE то планировщик может вызвать задачу раньше, чем данные будут доступны, выполняя тем самым ненужную работу.

Следует так же указать, что с помощью очередей ожидания реализация системного вызова poll(2)

становится более простой.

static unsigned int rtc_poll(struct file *file, poll_table *wait) { unsigned long l;

poll_wait(file, &rtc_wait, wait);

spin_lock_irq(&rtc_lock); l = rtc_irq_data; spin_unlock_irq(&rtc_lock);

if (l != 0) return POLLIN | POLLRDNORM; return 0; }

Вся работа выполняется независимой от типа устройства функцией poll_wait(), которая выполняет необходимые манипуляции; все что требуется сделать - это указать очередь,, которую следует "разбудить" обработчиком прерываний от устройства.




- Начало -  - Назад -  - Вперед -