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