Синхронный ввод-вывод
Самым простым механизмом вызова функций драйвера был бы косвенный вызов
соответствующих процедур, составляющих тело драйвера, подобно тому, как
это делается в MS DOS и ряде других однозадачных систем.
В системах семейства Unix драйвер последовательного устройства исполняется
в рамках той нити, которая сформировала запрос, хотя и с привилегиями
ядра. Ожидая реакции устройства, драйвер переводит процесс в состояние
ожидания доступными ему примитивами работы с планировщиком. В примере
10.1 это interruptibie_sieep_on. В качестве
параметра этой функции передается блок переменных состояния устройства,
и в этом блоке сохраняется ссылка на контекст блокируемой нити.
Доступные прикладным программам функции драйвера исполняются в пользовательском
контексте — в том смысле, что, хотя драйвер и работает в адресном пространстве
ядра, но при его работе определено и пользовательское адресное пространство,
поэтому он может пользоваться примитивами
Обмена данными С НИМ (в примере 10.1 это memcpy_from_fs).
Обработчик прерывания наоборот работает в контексте прерывания, когда
пользовательское адресное пространство не определено. Поэтому, чтобы при
обслуживании прерывания можно было получить доступ к пользовательским
данным, основная нить драйвера вынуждена копировать их в буфер в адресном
пространстве ядра.
Синхронная модель драйвера очень проста в реализации, но имеет существенный
недостаток, приведенный в примере 10.1, — драйвер нереентерабелен. Обращение
двух нитей к одному устройству приведет к непредсказуемым последствиям
(впрочем, для практических целей достаточно того, что среди возможных
последствий числится нарушение целостности данных и последующая паника
регистров на экране). Предсказуемость последствий обеспечивается включением
в контекст устройства семафора, установкой этого семафора при входе в
функцию foo_write и снятием его при выходе. Семафор имеет очередь ожидающих
его процессов, и, таким образом, реентрантно (т. е. во время обработки
предыдущего аналогичного запроса) приходящие запросы будут устанавливаться
в очередь.
Альтернативный подход к организации ввода-вывода состоит в том, чтобы возложить
работу по формированию очереди запросов не на драйвер, а на функцию предобработки
запроса. При этом первый запрос к драйверу, какое-то время бывшему неактивным,
может по-прежнему осуществляться в нити процесса, сформировавшего этот запрос,
но все последующие запросы извлекаются из очереди fork-процессом драйвера
при завершении предыдущего запроса. Такой подход называется асинхронным.
Примечание
Здесь возникает интересный вопрос: если запрос обрабатывается асинхронно,
то обязана ли пользовательская программа ожидать окончания операции? Вообще
говоря, не обязана, но этот вопрос подробнее будет обсуждаться в разд.
Содержание раздела