- •Глава 10. Подсистема управления вводом-выводом
- •10.1 Взаимодействие драйверов с программной и аппаратной средой
- •10.1.1 Конфигурация системы
- •10.1.2 Системные функции и взаимодействие с драйверами
- •1. Просматривается таблица файлов для того, чтобы убедиться в том, что ни
- •2. Если устройство символьного типа, ядро запускает процедуру закрытия уст-
- •Ibm 370 имеется инструкция "Start I/o" (Начать ввод-вывод), которая иниции-
- •10.1.2.4 Стратегический интерфейс
- •10.1.2.5 Ioctl
- •Ioctl(fd,command,arg);
- •10.1.2.6 Другие функции, имеющие отношение к файловой системе
- •10.1.3 Программы обработки прерываний
- •5, Как пользуясь блочным интерфейсом, так и не прибегая к структурированию
- •0, Младший - 21. Файл "/dev/rdsk15" соответствует устройству посимвольного
- •10.3 Терминальные драйверы
- •Ioctl. Когда соответствующие критерии удовлетворены, программа обработки
- •Ioctl для того, чтобы перевести терминал в режим без обработки: он отключает
- •10.3.5 Назначение операторского терминала
- •10.3.6 Драйвер косвенного терминала
- •10.3.7 Вход в систему
- •10.4 Потоки
- •10.4.2 Анализ потоков
10.1.2 Системные функции и взаимодействие с драйверами
В этом разделе рассматривается взаимодействие ядра с драйверами устрой-
ств. При выполнении тех системных функций, которые используют дескрипторы
файлов, ядро, следуя за указателями, хранящимися в пользовательском дескрип-
торе файла, обращается к таблице
+-----------------------------------------------+
| таблица ключей устройств ввода-вывода блоками |
+-------+--------+---------+--------------------+
| вход | open | close | strategy |
+-------+--------+---------+--------------------+
| 0 | gdopen | gdclose | gdstrategy |
+-------+--------+---------+--------------------+
| 1 | gtopen | gtclose | gtstrategy |
+-------+--------+---------+--------------------+
+----------------------------------------------------------------+
| таблица ключей устройств посимвольного ввода-вывода |
+------+-----------+-----------+---------+-----------+-----------+
| вход | open | close | read | write | ioctl |
+------+-----------+-----------+---------+-----------+-----------+
| 0 | conopen | conclose | conread | conwrite | conioctl |
+------+-----------+-----------+---------+-----------+-----------+
| 1 | dzbopen | dzbclose | dzbread | dzbwrite | dzbioctl |
+------+-----------+-----------+---------+-----------+-----------+
| 2 | syopen | nulldev | syread | sywrite | syioctl |
+------+-----------+-----------+---------+-----------+-----------+
| 3 | nulldev | nulldev | mmread | mmwrite | nodev |
+------+-----------+-----------+---------+-----------+-----------+
| 4 | gdopen | gdclose | gdread | gdwrite | nodev |
+------+-----------+-----------+---------+-----------+-----------+
| 5 | gtopen | gtclose | gtread | gtwrite | nodev |
+------+-----------+-----------+---------+-----------+-----------+
Рисунок 10.2. Пример заполнения таблиц ключей устройств ввода-
вывода блоками и символами
файлов ядра и к индексу, где оно проверяет тип файла, и переходит к таблице
ключей устройств ввода-вывода блоками или символами. Ядро извлекает из ин-
декса старший и младший номера устройства, использует старший номер в качес-
тве указателя на точку входа в соответствующей таблице и вызывает выполнение
функции драйвера в соответствии с выполняемой системной функцией, передавая
младший номер в качестве параметра. Важным различием в реализации системных
функций для файлов устройств и для файлов обычного типа является то, что ин-
декс специального файла не блокируется в то время, когда ядро выполняет
программу драйвера. Драйверы часто приостанавливают свою работу, ожидая свя-
зи с аппаратными средствами или поступления данных, поэтому ядро не в состо-
293
янии определить, на какое время процесс будет приостановлен. Если индекс
заблокирован, другие процессы, обратившиеся к индексу (например, посредством
системной функции stat), приостановятся на неопределенное время, поскольку
один процесс приостановил драйвер.
Драйвер устройства интерпретирует параметры вызова системной функции в
отношении устройства. Драйвер поддерживает структуры данных, описывающие
состояние каждой контролируемой единицы данного типа устройства; функции
драйвера и программы обработки прерываний реализуются в соответствии с сос-
тоянием драйвера и с тем, какое действие выполняется в этот момент (напри-
мер, данные вводятся или выводятся). Теперь рассмотрим каждый интерфейс бо-
лее подробно.
+------------------------------------------------------------+
| алгоритм open /* для драйверов устройств */ |
| входная информация: имя пути поиска |
| режим открытия |
| выходная информация: дескриптор файла |
| { |
| преобразовать имя пути поиска в индекс, увеличить значе-|
| ние счетчика ссылок в индексе; |
| выделить в таблице файлов место для пользовательского |
| дескриптора файла, как при открытии обычного файла; |
| |
| выбрать из индекса старший и младший номера устройства; |
| |
| сохранить контекст (алгоритм setjmp) в случае передачи |
| управления от драйвера; |
| |
| если (устройство блочного типа) |
| { |
| использовать старший номер устройства в качестве ука-|
| зателя в таблице ключей устройств ввода-вывода бло- |
| ками; |
| вызвать процедуру открытия драйвера по данному индек-|
| су: передать младший номер устройства, режимы откры-|
| тия; |
| } |
| в противном случае |
| { |
| использовать старший номер устройства в качестве ука-|
| зателя в таблице ключей устройств посимвольного вво-|
| да-вывода; |
| вызвать процедуру открытия драйвера по данному индек-|
| су: передать младший номер устройства, режимы откры-|
| тия; |
| } |
| |
| если (открытие в драйвере не выполнилось) |
| привести таблицу файлов к первоначальному виду, |
| уменьшить значение счетчика в индексе; |
| } |
+------------------------------------------------------------+
Рисунок 10.3. Алгоритм открытия устройства
10.1.2.1 Opeп
При открытии устройства ядро следует той же процедуре, что и при откры-
294
тии файлов обычного типа (см. раздел 5.1), выделяя в памяти индексы, увели-
чивая значение счетчика ссылок и присваивая значение точки входа в таблицу
файлов и пользовательского дескриптора файла. Наконец, ядро возвращает зна-
чение пользовательского дескриптора файла вызывающему процессу, так что отк-
рытие устройства выглядит так же, как и открытие файла обычного типа. Одна-
ко, перед тем, как вернуться в режим задачи, ядро запускает
зависящую от устройства процедуру open (Рисунок 10.3). Для устройства вво-
да-вывода блоками запускается процедура open, закодированная в таблице клю-
чей устройств ввода-вывода блоками, для устройств посимвольного ввода-вывода
- процедура open, закодированная в соответствующей таблице. Если устройство
имеет как блочный, так и символьный тип, ядро запускает процедуру open, со-
ответствующую типу файла устройства, открытого пользователем: обе процедуры
могут даже быть идентичны, в зависимости от конкретного драйвера.
Зависящая от типа устройства процедура open устанавливает связь между
вызывающим процессом и открываемым устройством и инициализирует информацион-
ные структуры драйвера. Например, процедура open для терминала может приос-
тановить процесс до тех пор, пока в машину не поступит сигнал (аппаратный) о
том, что пользователь предпринял попытку зарегистрироваться. После этого
инициализируются информационные структуры драйвера в соответствии с приняты-
ми установками терминала (например, скоростью передачи информации в бодах).
Для "программных устройств", таких как память системы, процедура open может
не включать в себя инициализацию.
Если во время открытия устройства процессу пришлось приостановиться по
какой-либо из внешних причин, может так случиться, что событие, которое дол-
жно было бы вызвать возобновление выполнения процесса, так никогда и не про-
изойдет. Например, если на данном терминале еще не зарегистрировался ни один
из пользователей, процесс getty, "открывший" терминал (раздел 7.9), приоста-
навливается до тех пор, пока пользователем не будет предпринята попытка ре-
гистрации, при этом может пройти достаточно большой промежуток времени. Ядро
должно иметь возможность возобновить выполнение процесса и отменить вызов
функции open по получении сигнала: ему следует сбросить индекс, отменить
точку входа в таблице файлов и пользовательский дескриптор файла, которые
были выделены перед входом в драйвер, поскольку открытие не произошло. Ядро
сохраняет контекст процесса, используя алгоритм setjmp (раздел 6.4.4), преж-
де чем запустить процедуру open; если процесс возобновляется по сигналу, яд-
ро восстанавливает контекст процесса в том состоянии, которое он имел перед
обращением к драйверу, используя алгоритм longjmp (раздел 6.4.4), и возвра-
щает системе все выделенные процедуре open структуры данных. Точно так же и
драйвер может уловить сигнал и очистить доступные ему структуры данных, если
это необходимо. Ядро также переустанавливает структуры данных файловой сис-
темы, когда драйвер сталкивается с исключительными ситуациями, такими, как
попытка пользователя обратиться к устройству, отсутствующему в данной конфи-
гурации. В подобных случаях функция open не выполняется.
Процессы могут указывать значения различных параметров, характеризующие
особенности выполнения процедуры открытия. Из них наиболее часто использует-
ся "no delay" (без задержки), означающее, что процесс не будет приостановлен
во время выполнения процедуры open, если устройство не готово. Системная
функция open возвращает управление немедленно и пользовательский процесс не
узнает, произошло ли аппаратное соединение или нет. Открытие устройства с
параметром "no delay", кроме всего прочего, затронет семантику вызова функ-
ции read, что мы увидим далее (раздел 10.3.4).
Если устройство открывается многократно, ядро обрабатывает пользователь-
ские дескрипторы файлов, индекс и записи в таблице файлов так, как это опи-
сано в главе 5, запуская определяемую типом устройства процедуру open при
каждом вызове системной функции open. Таким образом, драйвер устройства мо-
жет подсчитать, сколько раз устройство было "открыто", и прервать выполнение
функции open, если количество открытий приняло недопустимое значение. Напри-
295
мер, имеет смысл разрешить процессам многократно "открывать" терминал на за-
пись для того, чтобы пользователи могли обмениваться сообщениями. Но при
этом не следует допускать многократного "открытия" печатающего устройства
для одновременной записи, так как процессы могут затереть друг другу инфор-
мацию. Эти различия имеют смысл скорее на практике, нежели на стадии разра-
ботки: разрешение одновременной записи на терминалы способствует установле-
нию взаимодействия между пользователями; запрещение одновременной записи на
принтеры служит повышению читабельности машинограмм (**).
+------------------------------------------------------------+
| алгоритм close /* для устройств */ |
| входная информация: дескриптор файла |
| выходная информация: отсутствует |
| { |
| выполнить алгоритм стандартного закрытия (глава 5ххх); |
| если (значение счетчика ссылок в таблице файлов не 0) |
| перейти на finish; |
| если (существует еще один открытый файл, старший и млад-|
| ший номера которого совпадают с номерами закрываемого |
| устройства) |
| перейти на finish; /* не последнее закрытие */ |
| если (устройство символьного типа) |
| { |
| использовать старший номер в качестве указателя в |
| таблице ключей устройства посимвольного ввода-выво- |
| да; |
| вызвать процедуру закрытия, определяемую типом драй- |
| вера и передать ей в качестве параметра младший но- |
| мер устройства; |
| } |
| если (устройство блочного типа) |
| { |
| если (устройство монтировано) |
| перейти на finish; |
| переписать блоки устройства из буферного кеша на уст-|
| ройство; |
| использовать старший номер в качестве указателя в |
| таблице ключей устройства ввода-вывода блоками; |
| вызвать процедуру закрытия, определяемую типом драй- |
| вера и передать ей в качестве параметра младший но- |
| мер устройства; |
| сделать недействительными блоки устройства, оставшие-|
| ся в буферном кеше; |
| } |
| finish: |
| освободить индекс; |
| } |
+------------------------------------------------------------+
Рисунок 10.4. Алгоритм закрытия устройства
----------------------------------------
(**) На практике вывод на печать обычно управляется специальными процессами
буферизации, и права доступа устанавливаются таким образом, чтобы толь-
ко система буферизации могла обращаться к принтеру.
296
10.1.2.2 Closе
Процесс разрывает связь с открытым устройством, закрывая его. Однако,
ядро запускает определяемую типом устройства процедуру close только в пос-
леднем вызове функции close для этого устройства, и то только если не оста-
лось процессов, которым устройство необходимо открытым, поскольку процедура
закрытия устройства завершается разрывом аппаратного соединения; отсюда яс-
но, что ядру следует подождать, пока не останется ни одного процесса, обра-
щающегося к устройству. Поскольку ядро запускает процедуру открытия устройс-
тва при каждом вызове системной функции open, а процедуру закрытия только
один раз, драйверу устройства неведомо, сколько процессов используют устрой-
ство в данный момент. Драйверы могут легко выйти из строя, если при их напи-
сании не соблюдалась осторожность: когда при выполнении процедуры close они
приостанавливают свою работу и какой-нибудь процесс открывает устройство до
того, как завершится процедура закрытия, устройство может стать недоступным
для работы, если в результате комбинации вызовов open и close сложилась не-
распознаваемая ситуация.
Алгоритм закрытия устройства похож на алгоритм закрытия файла обычного
типа (Рисунок 10.4). Однако, до того, как ядро освобождает индекс, в нем вы-
полняются действия, специфичные для файлов устройств.