Что ERESTARTSYS использовал при написании драйвера для Linux? - PullRequest
28 голосов
/ 06 марта 2012

Я узнаю о блокирующих функциях ввода-вывода для написания драйвера устройства linux, и мне интересно, как используется ERESTARTSYS.Рассмотрим следующее:

Глобальная переменная:

wait_queue_head_t my_wait_q_head;
int read_avail = 0;


device_init ():

init_waitqueue_head(&my_wait_q_head);


device_read ():

printk("I'm inside driver read!\n");
wait_event_interruptible(&my_wait_q_head, read_avail != 0);
printk("I'm awaken!\n");


device_write ():

read_avail = 1;
wake_up_interruptible(&my_wait_q_head);


Когда я вызываю read() из пространства пользователя, командная строка зависает до тех пор, пока я не вызову write() как и ожидалось.Сообщения printk появляются соответственно и в dmesg.Однако я вижу некоторые драйверы, написанные так:

Другая версия device_read ():

printk("I'm inside driver read!\n");
if(wait_event_interruptible(&my_wait_q_head, read_avail != 0))    
{return -ERESTARTSYS;}
printk("I'm awaken!\n");

Я тестировал вторую версию device_read(), используя тот же метод впользовательское пространство, и результат точно такой же, так что же использовать ERESTARTSYS?

p / s: я читал книгу Драйвер устройства Linux на этом, но я не понимаю, может кто-топривести пример для eleborate?:

Как только мы закончим этот призыв, что-то разбудило нас, но мы не знаем, что.Одна возможность состоит в том, что процесс получил сигнал.Оператор if, который содержит вызов wait_event_interruptible, проверяет этот случай.Это утверждение обеспечивает правильную и ожидаемую реакцию на сигналы, которые могли быть причиной пробуждения процесса (так как мы были в прерывающемся сне).Если поступил сигнал, и он не был заблокирован процессом, правильное поведение - позволить верхним уровням ядра обрабатывать событие.Для этого драйвер возвращает -ERESTARTSYS вызывающей стороне;это значение используется внутри уровня виртуальной файловой системы (VFS), который либо перезапускает системный вызов, либо возвращает -EINTR в пространство пользователя.Мы используем один и тот же тип проверки для обработки сигналов для каждой реализации чтения и записи.

Источник: http://www.makelinux.net/ldd3/chp-6-sect-2

1 Ответ

49 голосов
/ 06 марта 2012

-ERESTARTSYS связан с концепцией перезапускаемого системного вызова. Перезапускаемый системный вызов - это тот, который может быть прозрачно повторно выполнен ядром, когда есть некоторое прерывание.

Например, процесс пользовательского пространства, который спит в системном вызове, может получить сигнал, выполнить обработчик, а затем, когда обработчик возвращается, он возвращается в ядро ​​и продолжает спать в исходном системном вызове.

Используя флаг POSIX sigaction API SA_RESTART, процессы могут организовать поведение перезапуска, связанное с сигналами.

В ядре Linux, когда драйвер или другой модуль, блокирующий в контексте системного вызова, обнаруживает, что задача была разбужена из-за сигнала, он может вернуть -EINTR. Но -EINTR будет пузыриться в пространстве пользователя и заставит системный вызов вернуть -1 с errno, установленным в EINTR.

Если вместо этого вы возвращаете -ERESTARTSYS, это означает, что ваш системный вызов перезапускается. Код ERESTARTSYS не обязательно будет виден в пространстве пользователя. Он либо переводится в значение возврата -1, а errno устанавливается в EINTR (тогда, очевидно, он виден в пространстве пользователя), либо переводится в поведение перезапуска системного вызова, что означает, что ваш системный вызов снова вызывается с теми же аргументами (посредством никаких действий со стороны пользовательского пространства: ядро ​​делает это, сохраняя информацию в специальном блоке перезапуска).

Обратите внимание на очевидную проблему с «одинаковыми аргументами» в предыдущем абзаце: некоторые системные вызовы не могут быть перезапущены с теми же параметрами, потому что они не являются идемпотентными! Например, предположим, что есть звонок в режиме сна, такой как nanosleep, на 5,3 секунды. Он прерывается через 5 секунд. Если он перезапустится наивно, он будет спать еще 5,3 секунды. Он должен передать новые параметры перезапущенному вызову в спящий режим только на оставшиеся 0,3 секунды; т.е. изменить содержимое блока перезапуска. Есть способ сделать это: вы вводите разные аргументы в блок перезапуска задачи и используете возвращаемое значение -ERESTART_RESTARTBLOCK.

Чтобы ответить на второй вопрос: в чем разница? Почему бы просто не написать процедуру чтения без проверки возвращаемого значения и возврата -ERESTARTSYS? Ну, потому что это неправильно в случае, если пробуждение происходит из-за сигнала! Вы хотите, чтобы чтение возвращало 0 байтов при каждом поступлении сигнала? Это может быть неправильно истолковано пользовательским пространством как конец данных. Такая проблема не будет обнаруживаться в тестовых случаях, которые не используют сигналы.

...