The design of the unix operating system by Maurice J

Вид материалаРеферат
10.1 Взаимодействие драйверов с программной и аппаратной средой
10.2 Дисковые драйверы
Подобный материал:
1   ...   36   37   38   39   40   41   42   43   ...   55

щить статистику процессов. Еще один пример: драйверы могут вести

трассировку записей в удобном для отладки виде, а драйвер трасси-

ровки дает возможность пользователям читать эти записи. Наконец,

профиль ядра, рассмотренный в главе 8, выполнен как драйвер: про-

цесс записывает адреса программ ядра, обнаруженных в таблице

идентификаторов ядра, и читает результаты профилирования.

В этой главе рассматривается взаимодействие между процессами

и подсистемой управления вводом-выводом, а также между машиной и

драйверами устройств. Исследуется общая структура и функциониро-

вание драйверов и в качестве примеров общего взаимодействия расс-

матриваются дисковые и терминальные драйверы. Завершает главу

описание нового метода реализации драйверов потоковых устройств.


10.1 ВЗАИМОДЕЙСТВИЕ ДРАЙВЕРОВ С ПРОГРАММНОЙ И АППАРАТНОЙ СРЕДОЙ


В системе UNIX имеется два типа устройств - устройства ввода-

вывода блоками и устройства неструктурированного или посимвольно-

го ввода-вывода. Как уже говорилось в главе 2, устройства вво-

да-вывода блоками, такие как диски и ленты, для остальной части

системы выглядят как запоминающие устройства с произвольной вы-

боркой; к устройствам посимвольного ввода-вывода относятся все

другие устройства, в том числе терминалы и сетевое оборудование.

Устройства ввода-вывода блоками могут иметь интерфейс и с уст-

ройствами посимвольного ввода-вывода.

Пользователь взаимодействует с устройствами через посредни-

чество файловой системы (см. Рисунок 2.1). Каждое устройство име-

ет имя, похожее на имя файла, и пользователь обращается к нему

как к файлу. Специальный файл устройства имеет индекс и занимает

место в иерархии каталогов файловой системы. Файл устройства от-

личается от других файлов типом файла, хранящимся в его индексе,

либо "блочный", либо "символьный специальный", в зависимости от

устройства, которое этот файл представляет. Если устройство имеет


как блочный, так и символьный интерфейс, его представляют два

файла: специальный файл устройства ввода-вывода блоками и специ-

альный файл устройства посимвольного ввода-вывода. Системные

функции для обычных файлов, такие как open, close, read и write,

имеют то же значение и для устройств, в чем мы убедимся позже.

Системная функция ioctl предоставляет процессам возможность уп-

равлять устройствами посимвольного ввода-вывода, но не применима

в отношении к файлам обычного типа (*). Тем не менее, драйверам

устройств нет необходимости поддерживать полный набор системных

функций. Например, вышеупомянутый драйвер трассировки дает про-

цессам возможность читать записи, созданные другими драйверами,

но не позволяет создавать их.


10.1.1 Конфигурация системы


Задание конфигурации системы это процедура указания админист-

раторами значений параметров, с помощью которых производится

настройка системы. Некоторые из параметров указывают размеры таб-

лиц ядра, таких как таблица процессов, таблица индексов и таблица

файлов, а также сколько буферов помещается в буферном пуле. С по-

мощью других параметров указывается конфигурация устройств, то

есть производятся конкретные указания ядру, какие устройства

включаются в данную системную реализацию и их "адрес". Например,

в конфигурации может быть указано, что терминальная плата встав-


----------------------------------------

(*) И наоборот, системная функция fcntl обеспечивает контроль над

действиями, производимыми на уровне дескриптора файла, но не

на уровне устройства. В других реализациях функция ioctl при-

менима для файлов всех типов.


лена в соответствующий разъем на аппаратной панели.

Существует три стадии, на которых может быть указана конфигу-

рация устройств. Во-первых, администраторы могут кодировать ин-

формацию о конфигурации в файлах, которые транслируются и компо-

нуются во время построения ядра. Информация о конфигурации обычно

указывается в простом формате, и программа конфигурации преобра-

зует ее в файл, готовый для трансляции. Во-вторых, администраторы

могут указывать информацию о конфигурации после того, как система

уже запущена; ядро динамически корректирует внутренние таблицы

конфигурации. Наконец, самоидентифицирующиеся устройства дают яд-

ру возможность узнать, какие из устройств включены. Ядро считыва-

ет аппаратные ключи для самонастройки. Подробности задания сис-

темной конфигурации выходят за пределы этой книги, однако во всех


случаях результатом процедуры задания конфигурации является гене-

рация или заполнение таблиц, составляющих основу программ ядра.

Интерфейс "ядро - драйвер" описывается в таблице ключей

устройств ввода-вывода блоками и в таблице ключей устройств по-

символьного ввода-вывода (Рисунок 10.1). Каждый тип устройства

имеет в таблице точки входа, которые при выполнении системных

функций адресуют ядро к соответствующему драйверу. Функции open и

close, вызываемые файлом устройства, "пропускаются" через таблицы

ключей устройств в соответствии с типом файла. Функции mount и

umount так же вызывают выполнение процедур открытия и закрытия

устройств, но для устройств ввода-вывода блоками. Функции read и

write, вызываемые устройствами ввода-вывода блоками и файлами в

смонтированных файловых системах, запускают алгоритмы работы с

буферным кешем, инициирующие реализацию стратегической процедуры

работы с устройствами. Некоторые из драйверов запускают эту про-

цедуру изнутри из процедур чтения и записи. Более подробно взаи-

модействие с каждым драйвером рассматривается в следующем разде-

ле.

Интерфейс "аппаратура - драйвер" состоит из машинно-зависимых

управляющих регистров или команд ввода-вывода для управления уст-

ройствами и векторами прерываний: когда происходит прерывание от

устройства, система идентифицирует устройство, вызвавшее прерыва-

ние, и запускает программу обработки соответствующего прерывания.

Очевидно, что "программные устройства", такие как драйвер системы

построения профиля ядра (глава 8) не имеют аппаратного интерфей-

са, однако программы обработки других прерываний могут обращаться

к "обработчику программного прерывания" непосредственно. Напри-

мер, программа обработки прерывания по таймеру обращается к прог-

рамме обработки прерывания системы построения профиля ядра.

Администраторы устанавливают специальные файлы устройств ко-

мандой mknod, в которой указывается тип файла (блочный или сим-

вольный), старший и младший номера устройства. Команда mknod за-

пускает выполнение системной функции с тем же именем, создающей

файл устройства. Например, в командной строке

mknod /dev/tty13 c 2 13

"/dev/tty13" - имя файла устройства, "c" указывает, что тип файла

- "символьный специальный" ("b", соответственно, блочный), "2" -

старший номер устройства, "13" - младший номер устройства. Стар-

ший номер устройства показывает его тип, которому соответствует

точка входа в таблице ключей устройств, младший номер устройства

- это порядковый номер единицы устройства данного типа. Если про-

цесс открывает специальный блочный файл с именем "/dev/dsk1" и

кодом 0, ядро запускает программу gdopen в точке 0 таблицы ключей

устройств блочного ввода-вывода (Рисунок 10.2); если процесс чи-

тает специальный символьный файл с именем "/dev/mem" и кодом 3,


Подсистема управления файлами

-------------------------------------------------------------┐

│ open close │

│ open close read write ioctl read write │

│ mount umount │

L--+-----+----+-----+-----+------------+-----+-----+-----+----

│ │ │ │ │ │ │ --+-----+---┐

│ │ │ │ │ │ │ │ функции │

│ │ │ │ │ │ │ │ работы с │

│ │ │ │ │ │ │ │ буферным │

│ │ │ │ │ │ │ │ кешем │

│ │ │ │ │ │ │ L-----T------

│ │ │ │ │ │ │ │

---+-----+----+-----+-----+-┐ -+-----+---------+-----┐

│ Таблица ключей устройств │ │ Таблица ключей уст- │

│ посимвольного ввода-вывода│ │ ройств ввода-вывода │

│ │ │ блоками │

L--T-----T----T-----T-----T-- L--T------T--------T----

│ │ │ │ │ │ │ │

│ │ │ │ │ │ │ │

---+-----+----+-----+-----+-┐ ---+------+--------+---┐

│open close read write ioctl│ Точки │ open close strategy│

│ │ входа │ │

│ Драйвер │ для │ Драйвер │

│ │ драй- │ │

│программа обработки преры- │ веров │ программа обработки │

│ ваний от устройства │ │прерываний от устройст│

L------------+--------------- L-----------+-----------

│ │

---------+----------┐ ----------+---------┐

│ Вектор прерывания │ │ Вектор прерывания │

L--------T----------- L---------T----------

│ │

L-------------------T-----------------



Прерывания от устройств


Рисунок 10.1. Точки входа для драйверов


ядро запускает программу mmread в точке 3 таблицы ключей уст-

ройств посимвольного ввода-вывода. Программа nulldev - это "пус-

тая" программа, используемая в тех случаях, когда отсутствует не-

обходимость в конкретной функции драйвера. С одним старшим

номером устройства может быть связано множество периферийных уст-

ройств; младший номер устройства позволяет отличить их одно от

другого. Не нужно создавать специальные файлы устройств при каж-

дой загрузке системы; их только нужно корректировать, если изме-

нилась конфигурация системы, например, если к установленной кон-

фигурации были добавлены устройства.


10.1.2 Системные функции и взаимодействие с драйверами


В этом разделе рассматривается взаимодействие ядра с драйве-

рами устройств. При выполнении тех системных функций, которые ис-

пользуют дескрипторы файлов, ядро, следуя за указателями, храня-

щимися в пользовательском дескрипторе файла, обращается к таблице


------------------------------------------------┐

│ таблица ключей устройств ввода-вывода блоками │

+-------T--------T---------T--------------------+

│ вход │ open │ close │ strategy │

+-------+--------+---------+--------------------+

│ 0 │ gdopen │ gdclose │ gdstrategy │

+-------+--------+---------+--------------------+

│ 1 │ gtopen │ gtclose │ gtstrategy │

L-------+--------+---------+---------------------

-----------------------------------------------------------------┐

│ таблица ключей устройств посимвольного ввода-вывода │

+------T-----------T-----------T---------T-----------T-----------+

│ вход │ 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 │

L------+-----------+-----------+---------+-----------+------------


Рисунок 10.2. Пример заполнения таблиц ключей устройств ввода-

вывода блоками и символами


файлов ядра и к индексу, где оно проверяет тип файла, и переходит

к таблице ключей устройств ввода-вывода блоками или символами.

Ядро извлекает из индекса старший и младший номера устройства,

использует старший номер в качестве указателя на точку входа в

соответствующей таблице и вызывает выполнение функции драйвера в

соответствии с выполняемой системной функцией, передавая младший

номер в качестве параметра. Важным различием в реализации систем-

ных функций для файлов устройств и для файлов обычного типа явля-

ется то, что индекс специального файла не блокируется в то время,

когда ядро выполняет программу драйвера. Драйверы часто приоста-

навливают свою работу, ожидая связи с аппаратными средствами или

поступления данных, поэтому ядро не в состоянии определить, на

какое время процесс будет приостановлен. Если индекс заблокиро-

ван, другие процессы, обратившиеся к индексу (например, посредс-

твом системной функции stat), приостановятся на неопределенное

время, поскольку один процесс приостановил драйвер.

Драйвер устройства интерпретирует параметры вызова системной

функции в отношении устройства. Драйвер поддерживает структуры

данных, описывающие состояние каждой контролируемой единицы дан-

ного типа устройства; функции драйвера и программы обработки пре-

рываний реализуются в соответствии с состоянием драйвера и с тем,

какое действие выполняется в этот момент (например, данные вво-

дятся или выводятся). Теперь рассмотрим каждый интерфейс более

подробно.


10.1.2.1 Open


При открытии устройства ядро следует той же процедуре, что и

при открытии файлов обычного типа (см. раздел 5.1), выделяя в па-

мяти индексы, увеличивая значение счетчика ссылок и присваивая

значение точки входа в таблицу файлов и пользовательского деск-

риптора файла. Наконец, ядро возвращает значение пользовательско-

го дескриптора файла вызывающему процессу, так что открытие уст-

ройства выглядит так же, как и открытие файла обычного типа.


-------------------------------------------------------------┐

│ алгоритм open /* для драйверов устройств */ │

│ входная информация: имя пути поиска │

│ режим открытия │

│ выходная информация: дескриптор файла │

│ { │

│ преобразовать имя пути поиска в индекс, увеличить значе-│

│ ние счетчика ссылок в индексе; │

│ выделить в таблице файлов место для пользовательского │

│ дескриптора файла, как при открытии обычного файла; │

│ │

│ выбрать из индекса старший и младший номера устройства; │

│ │

│ сохранить контекст (алгоритм setjmp) в случае передачи │

│ управления от драйвера; │

│ │

│ если (устройство блочного типа) │

│ { │

│ использовать старший номер устройства в качестве ука-│

│ зателя в таблице ключей устройств ввода-вывода бло- │

│ ками; │

│ вызвать процедуру открытия драйвера по данному индек-│

│ су: передать младший номер устройства, режимы откры-│

│ тия; │

│ } │

│ в противном случае │

│ { │

│ использовать старший номер устройства в качестве ука-│

│ зателя в таблице ключей устройств посимвольного вво-│

│ да-вывода; │

│ вызвать процедуру открытия драйвера по данному индек-│

│ су: передать младший номер устройства, режимы откры-│


│ тия; │

│ } │

│ │

│ если (открытие в драйвере не выполнилось) │

│ привести таблицу файлов к первоначальному виду, │

│ уменьшить значение счетчика в индексе; │

│ } │

L-------------------------------------------------------------


Рисунок 10.3. Алгоритм открытия устройства


зависящую от устройства процедуру 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, если количество открытий приняло недопустимое значение.

Например, имеет смысл разрешить процессам многократно "откры-

вать" терминал на запись для того, чтобы пользователи могли обме-

ниваться сообщениями. Но при этом не следует допускать многократ-

ного "открытия" печатающего устройства для одновременной записи,

так как процессы могут затереть друг другу информацию. Эти разли-

чия имеют смысл скорее на практике, нежели на стадии разработки:

разрешение одновременной записи на терминалы способствует уста-

новлению взаимодействия между пользователями; запрещение одновре-

менной записи на принтеры служит повышению читабельности машиног-

рамм (**).


10.1.2.2 Close


Процесс разрывает связь с открытым устройством, закрывая его.

Однако, ядро запускает определяемую типом устройства процедуру

close только в последнем вызове функции close для этого устройс-


----------------------------------------

(**) На практике вывод на печать обычно управляется специальными

процессами буферизации, и права доступа устанавливаются та-

ким образом, чтобы только система буферизации могла обра-

щаться к принтеру.


тва, и то только если не осталось процессов, которым устройство

необходимо открытым, поскольку процедура закрытия устройства за-

вершается разрывом аппаратного соединения; отсюда ясно, что ядру


следует подождать, пока не останется ни одного процесса, обращаю-

щегося к устройству. Поскольку ядро запускает процедуру открытия

устройства при каждом вызове системной функции open, а процедуру

закрытия только один раз, драйверу устройства неведомо, сколько

процессов используют устройство в данный момент. Драйверы могут

легко выйти из строя, если при их написании не соблюдалась осто-

рожность: когда при выполнении процедуры close они приостанавли-

вают свою работу и какой-нибудь процесс открывает устройство до

того, как завершится процедура закрытия, устройство может стать

недоступным для работы, если в результате комбинации вызовов open

и close сложилась нераспознаваемая ситуация.


-------------------------------------------------------------┐

│ алгоритм close /* для устройств */ │

│ входная информация: дескриптор файла │

│ выходная информация: отсутствует │

│ { │

│ выполнить алгоритм стандартного закрытия (глава 5ххх); │

│ если (значение счетчика ссылок в таблице файлов не 0) │

│ перейти на finish; │

│ если (существует еще один открытый файл, старший и млад-│

│ ший номера которого совпадают с номерами закрываемого │

│ устройства) │

│ перейти на finish; /* не последнее закрытие */ │

│ если (устройство символьного типа) │

│ { │

│ использовать старший номер в качестве указателя в │

│ таблице ключей устройства посимвольного ввода-выво- │

│ да; │

│ вызвать процедуру закрытия, определяемую типом драй- │

│ вера и передать ей в качестве параметра младший но- │

│ мер устройства; │

│ } │

│ если (устройство блочного типа) │

│ { │

│ если (устройство монтировано) │

│ перейти на finish; │

│ переписать блоки устройства из буферного кеша на уст-│

│ ройство; │

│ использовать старший номер в качестве указателя в │

│ таблице ключей устройства ввода-вывода блоками; │

│ вызвать процедуру закрытия, определяемую типом драй- │

│ вера и передать ей в качестве параметра младший но- │

│ мер устройства; │

│ сделать недействительными блоки устройства, оставшие-│

│ ся в буферном кеше; │

│ } │

│ finish: │

│ освободить индекс; │

│ } │

L-------------------------------------------------------------


Рисунок 10.4. Алгоритм закрытия устройства


Алгоритм закрытия устройства похож на алгоритм закрытия файла

обычного типа (Рисунок 10.4). Однако, до того, как ядро освобож-

дает индекс, в нем выполняются действия, специфичные для файлов

устройств.

1. Просматривается таблица файлов для того, чтобы убедиться в

том, что ни одному из процессов не требуется, чтобы устройс-

тво было открыто. Чтобы установить, что вызов функции close

для устройства является последним, недостаточно положиться на

значение счетчика ссылок в таблице файлов, поскольку несколь-

ко процессов могут обращаться к одному и тому же устройству,

используя различные точки входа в таблице файлов. Так же не-

достаточно положиться на значение счетчика в таблице индек-

сов, поскольку одному и тому же устройству могут соответство-

вать несколько файлов устройства. Например, команда ls -l

покажет, что одному и тому же устройству символьного типа

("c" в начале строки) соответствуют два файла устройства,

старший и младший номера у которых (9 и 1) совпадают. Значе-

ние счетчика связей для каждого файла, равное 1, говорит о

том, что имеется два индекса.

crw--w--w- 1 root vis 9, 1 Aug 6 1984 /dev/tty01

crw--w--w- 1 root unix 9, 1 May 3 15:02 /dev/tty01

Если процессы открывают оба файла независимо один от другого,

они обратятся к разным индексам одного и того же устройства.

2. Если устройство символьного типа, ядро запускает процедуру

закрытия устройства и возвращает управление в режим задачи.

Если устройство блочного типа, ядро просматривает таблицу ре-

зультатов монтирования и проверяет, не располагается ли на

устройстве смонтированная файловая система. Если такая систе-

ма есть, ядро не сможет запустить процедуру закрытия устройс-

тва, поскольку не был сделан последний вызов функции close

для устройства. Даже если на устройстве нет смонтированной

файловой системы, в буферном кеше еще могут находиться блоки

с данными, оставшиеся от смонтированной ранее файловой систе-

мы и не переписанные на устройство, поскольку имели пометку

"отложенная запись". Поэтому ядро просматривает буферный кеш

в поисках таких блоков и переписывает их на устройство перед

запуском процедуры закрытия устройства. После закрытия уст-

ройства ядро вновь просматривает буферный кеш и делает не-

действительными все буферы, которые содержат блоки для только

что закрытого устройства, в то же время позволяя буферам с

актуальной информацией остаться в кеше.

3. Ядро освобождает индекс файла устройства.

Короче говоря, процедура закрытия устройства разрывает связь

с устройством и инициализирует заново информационные структуры

драйвера и аппаратную часть устройства с тем, чтобы ядро могло бы

позднее открыть устройство вновь.


10.1.2.3 Read и Write


Алгоритмы чтения и записи ядром на устройстве похожи на ана-

логичные алгоритмы для файлов обычного типа. Если процесс произ-

водит чтение или запись на устройстве посимвольного ввода-вывода,

ядро запускает процедуры read или write, определяемые типом драй-

вера. Несмотря на часто встречающиеся ситуации, когда ядро осу-

ществляет передачу данных непосредственно между адресным прост-

ранством задачи и устройством, драйверы устройств могут

буферизовать информацию внутри себя. Например, терминальные драй-

веры для буферизации данных используют символьные списки (раздел

10.3.1). В таких случаях драйвер устройства выделяет "буфер", ко-

пирует данные из пространства задачи при выполнении процедуры

write и выводит их из "буфера" на устройство. Процедура записи,

управляемая драйвером, регулирует объем выводимой информации

(т.н. управление потоком данных): если процессы генерируют инфор-

мацию быстрее, чем устройство выводит ее, процедура записи приос-

танавливает выполнение процессов до тех пор, пока устройство не

будет готово принять следующую порцию данных. При чтении драйвер

устройства помещает данные, полученные от устройства, в буфер и


Память


│ │

+-----+

160110│ CSR │ -------┐ ----------tty00

│ RDB +---------+ dz00 +-+---------tty01

│ TDB │ L------- │ ...

+-----+ L---------tty07

160120│ CSR │ -------┐ ----------tty08

160122│ RDB +---------+ dz01 +-+---------tty09

160126│ TDB │ L------- │ ...

+-----+ L---------tty15

│ │


Рисунок 10.5. Отображение в памяти ввода-вывода с использова-

нием контроллера VAX DZ11


копирует их из буфера в пользовательские адреса, указанные в вы-

зове системной функции.

Конкретный метод взаимодействия драйвера с устройством опре-

деляется особенностями аппаратуры. Некоторые из машин обеспечива-

ют отображение ввода-вывода в памяти, подразумевающее, что конк-

ретные адреса в адресном пространстве ядра являются не номерами

ячеек в физической памяти, а специальными регистрами, контролиру-

ющими соответствующие устройства. Записывая в указанные регистры

управляющие параметры в соответствии со спецификациями аппаратных

средств, драйвер осуществляет управление устройством. Например,

контроллер ввода-вывода для машины VAX-11 содержит специальные

регистры для записи информации о состоянии устройства (регистры

контроля и состояния) и для передачи данных (буферные регистры),

которые формируются по специальным адресам в физической памяти. В

частности, терминальный контроллер VAX DZ11 управляет 8 асинхрон-

ными линиями терминальной связи (см. [Levy 80], где более подроб-

но объясняется архитектура машин VAX). Пусть регистр контроля и

состояния (CSR) для конкретного терминала DZ11 имеет адрес

160120, передающий буферный регистр (TDB) - адрес 120126, а при-

нимающий буферный регистр (RDB) - адрес 160122 (Рисунок 10.5).

Для того, чтобы передать символ на терминал "/dev/tty09", драйвер

терминала записывает единицу (1 = 9 по модулю 8) в указанный дво-

ичный разряд регистра контроля и состояния и затем записывает

символ в передающий буферный регистр. Запись в передающий буфер-

ный регистр является передачей данных. Контроллер DZ11 выставляет

бит "выполнено" в регистре контроля и состояния, когда готов при-

нять следующую порцию данных. Дополнительно драйвер может выста-

вить бит "возможно прерывание передачи" в регистре контроля и

состояния, что заставляет контроллер DZ11 прерывать работу систе-

мы, когда он готов принять следующую порцию данных. Чтение данных

из DZ11 производится аналогично.


На других машинах имеется программируемый ввод-вывод, подра-

зумевающий, что в машине имеются инструкции по управлению уст-

ройствами. Драйверы управляют устройствами, выполняя

соответствующие инструкции. Например, в машине IBM 370 имеется

инструкция "Start I/O" (Начать ввод-вывод), которая инициирует

операцию ввода-вывода, связанную с устройством. Способ связи

драйвера с периферийными устройствами незаметен для пользователя.

Поскольку интерфейс между драйверами устройств и соответству-

ющими аппаратными средствами является машинно-зависимым, на этом

уровне не существует стандартных интерфейсов. Как в случае ввода-

вывода с отображением в памяти, так и в случае программируемого

ввода-вывода драйвер может посылать на устройство управляющие

последовательности с целью установления режима прямого доступа в

память (ПДП) для устройства. Система позволяет осуществлять мас-

совую передачу данных между устройством и памятью в режиме ПДП

параллельно с работой центрального процессора, при этом устройс-

тво прерывает работу системы по завершении передачи данных. Драй-

вер организует управление виртуальной памятью таким образом, что-

бы ячейки памяти с их действительными номерами использовались для

ПДП.

Быстродействующие устройства могут иногда передавать данные

непосредственно в адресное пространство задачи, без вмешательства

буфера ядра. В результате повышается скорость передачи данных,

поскольку при этом производится на одну операцию копирования

меньше, и, кроме того, объем данных, передаваемых за одну опера-

цию, не ограничивается размером буферов ядра. Драйверы, осущест-

вляющие такую передачу данных без "обработки", обычно используют

блочный интерфейс для процедур посимвольного чтения и записи, ес-

ли у них имеется двойник блочного типа.


10.1.2.4 Стратегический интерфейс


Ядро использует стратегический интерфейс для передачи данных

между буферным кешем и устройством, хотя, как уже говорилось ра-

нее, процедуры чтения и записи для устройств посимвольного ввода-

вывода иногда пользуются процедурой strategy (их двойника блочно-

го типа) для непосредственной передачи данных между устройством и

адресным пространством задачи. Процедура strategy может управлять

очередностью выполнения заданий на ввод-вывод, связанный с уст-

ройством, или выполнять более сложные действия по планированию

выполнения подобных заданий. Драйверы в состоянии привязывать пе-

редачу данных к одному физическому адресу или ко многим. Ядро пе-

редает адрес заголовка буфера стратегической процедуре драйвера;

в заголовке содержится список адресов (страниц памяти) и размеры

данных, передаваемых на или с устройства. Аналогичное действие

имеет место при работе механизма свопинга, описанного в главе 9.

При работе с буферным кешем ядро передает данные с одного адреса;

во время свопинга ядро передает данные, расположенные по несколь-

ким адресам (страницы памяти). Если данные копируются из или в

адресное пространство задачи, драйвер должен блокировать процесс

(или по крайней мере, соответствующие страницы) в памяти до за-

вершения передачи данных.

Например, после монтирования файловой системы ядро идентифи-

цирует каждый файл в файловой системе по номеру устройства и но-

меру индекса. В номере устройства закодированы его старший и

младший номера. Когда ядро обращается к блоку, который принадле-

жит файлу, оно копирует номер устройства и номер блока в заголо-

вок буфера, как уже говорилось ранее в главе 3. Обращения к дис-

ку, использующие алгоритмы работы с буферным кешем (например,

bread или bwrite), инициируют выполнение стратегической процеду-

ры, определяемой старшим номером устройства. Стратегическая про-

цедура использует значения полей младшего номера и номера блока

из заголовка буфера для идентификации места расположения данных

на устройстве, а адрес буфера - для идентификации места назначе-

ния передаваемых данных. Точно так же, когда процесс обращается к

устройству ввода-вывода блоками непосредственно (например, откры-

вая устройство и читая или записывая на него), он использует ал-

горитмы работы с буферным кешем, и интерфейс при этом функциони-

рует вышеописанным образом.


10.1.2.5 Ioctl


Системная функция ioctl является обобщением специфичных для

терминала функций stty (задать установки терминала) и gtty (полу-

чить установки терминала), имевшихся в ранних версиях системы

UNIX. Она выступает в качестве общей точки входа для всех связан-

ных с типом устройства команд и позволяет процессам задавать ап-

паратные параметры, ассоциированные с устройством, и программные

параметры, ассоциированные с драйвером. Специальные действия, вы-

полняемые функцией ioctl для разных устройств различны и опреде-

ляются типом драйвера. Программы, использующие вызов ioctl,

должны должны знать, с файлом какого типа они работают, так как

они являются аппаратно-зависимыми. Исключение из общего правила

сделано для системы, которая не видит различий между файлами раз-

ных типов. Более подробно использование функции ioctl для терми-

налов рассмотрено в разделе 10.3.3.

Синтаксис командной строки, содержащей вызов системной функ-

ции:

ioctl(fd,command,arg);

где fd - дескриптор файла, возвращаемый предварительно вызванной

функцией open, command - действие (команда), которое необходимо

выполнить драйверу, arg - параметр команды (может быть указателем

на структуру). Команды специфичны для различных драйверов; следо-

вательно, каждый драйвер интерпретирует команды в соответствии со

своими внутренними спецификациями, от команды, в свою очередь,

зависит формат структуры данных, описываемой передаваемым пара-

метром. Драйверы могут считывать структуру данных arg из прост-

ранства задачи в соответствии с предопределенным форматом или за-

писывать установки устройства в пространство задачи по адресу

указанной структуры. Например, наличие интерфейса, предоставляе-

мого функцией ioctl, дает возможность пользователям устанавливать

для терминала скорость передачи информации в бодах, перематывать

магнитную ленту, и, наконец, выполнять сетевые операции, задавая

номера виртуальных каналов и сетевые адреса.


10.1.2.6 Другие функции, имеющие отношение к файловой сис-

теме


Такие функции работы с файловой системой, как stat и chmod,

выполняются одинаково, как для обычных файлов, так и для уст-

ройств; они манипулируют с индексом, не обращаясь к драйверу.

Даже системная функция lseek работает для устройств. Например,

если процесс подводит головку на лентопротяжном устройстве к ука-

занному адресу смещения в байтах с помощью функции lseek, ядро

корректирует смещение в таблице файлов но не выполняет никаких

действий, специфичных для данного типа драйвера. Когда позднее

процесс выполняет чтение (read) или запись (write), ядро пересы-

лает адрес смещения из таблицы файлов в адресное пространство за-

дачи, подобно тому, как это имеет место при работе с файлами

обычного типа, и устройство физически перемещает головку к соот-

ветствующему смещению, указанному в пространстве задачи. Этот

случай иллюстрируется на примере в разделе 10.3.


Периферийные Соединительная Вектор

устройства панель прерывания

-------------------┐

│ │

tty00 -------------┐ │ │

tty01 .... │ │ │

... │ ---┐ +------------------+

tty07 -------------+-+ ││ ttyintr 0 │

tty08 -------------┐ +--+ +------------------+

tty09 .... +-+ ││ ttyintr 1 │

... ---------- +--+ +------------------+


tty15 ----- ---------+ ││ consintr │

консоль ------- +--+ +------------------+

принтер00 -------------T-+ ││ printintr 0 │

.... │ +--+ +------------------+

│ │ │ │ │

принтер03 -------------- │ │ │ │

│ │ │ │

L--- L-------------------


Рисунок 10.6. Прерывания от устройств


10.1.3 Программы обработки прерываний


Как уже говорилось выше (раздел 6.4.1), возникновение преры-

вания побуждает ядро запускать программу обработки прерываний, в

основе алгоритма которой лежит соотношение между устройством,

вызвавшим прерывание, и смещением в таблице векторов прерываний.

Ядро запускает программу обработки прерываний для данного типа

устройства, передавая ей номер устройства или другие параметры

для того, чтобы идентифицировать единицу устройства, вызвавшую

прерывание. Например, в таблице векторов прерываний на Рисунке

10.6 показаны две точки входа для обработки прерываний от терми-

налов ("ttyintr"), каждая из которых используется для обработки

прерываний, поступивших от 8 терминалов. Если устройство tty09

прервало работу системы, система вызывает программу обработки

прерывания, ассоциированную с местом аппаратного подключения уст-

ройства. Поскольку с одной записью в таблице векторов прерываний

может быть связано множество физических устройств, драйвер должен

уметь распознавать устройство, вызвавшее прерывание. На рисунке

записи в таблице векторов прерываний, соответствующие прерываниям

от терминалов, имеют метки 0 и 1, чтобы система различала их меж-

ду собой при вызове программы обработки прерываний, используя к

примеру этот номер в качестве передаваемого программе параметра.

Программа обработки прерываний использует этот номер и другую ин-

формацию, переданную механизмом прерывания, для того, чтобы удос-

товериться, что именно устройство tty09, а не tty12, прервало ра-

боту системы. Этот пример в упрощенном виде показывает то, что

имеет место в реальных системах, где на самом деле существует

несколько уровней контроллеров и соответствующих программ обра-

ботки прерываний, но он иллюстрирует общие принципы.

Если подвести итог, можно сказать, что номер устройства, ис-

пользуемый программой обработки прерываний, идентифицирует едини-

цу аппаратуры, а младший номер в файле устройства идентифицирует

устройство для ядра. Драйвер устройства устанавливает соответс-

твие между младшим номером устройства и номером единицы аппарату-

ры.


10.2 ДИСКОВЫЕ ДРАЙВЕРЫ


Так сложилось исторически, что дисковые устройства в системах

UNIX разбивались на разделы, содержащие различные файловые систе-

мы, что означало "деление [дискового] пакета на несколько управ-

ляемых по-своему частей" (см. [System V 84b]). Например, если на

диске располагаются четыре файловые системы, администратор может

оставить одну из них несмонтированной, одну смонтировать только

для чтения, а две других только для записи. Несмотря на то, что

все файловые системы сосуществуют на одном физическом устройстве,

пользователи не могут ни обращаться к файлам немонтированной фай-

ловой системы, используя методы доступа, описанные в главах 4 и

5, ни записывать файлы в файловые системы, смонтированные только

для чтения. Более того, так как каждый раздел (и, следовательно,

файловая система) занимает на диске смежные дорожки и цилиндры,

скопировать всю файловую систему легче, чем в том случае, если бы