The design of the unix operating system by Maurice J
Вид материала | Реферат |
10.1 Взаимодействие драйверов с программной и аппаратной средой 10.2 Дисковые драйверы |
- Лекция 10. Файловые системы Unix, 116.79kb.
- Уровни рассмотрения, 314.07kb.
- Курс по операционным системам (на примере ос windows) Основан на учебном курсе Windows, 29.21kb.
- Выполнил ученик 11 «А» класса, 443.51kb.
- Ос лекция 1 (2-й семестр – временно), 101.4kb.
- Operating System, 7686.97kb.
- Unix-подобные операционные системы, характеристики, особенности, разновидности, 40.63kb.
- 1. ms sql server. Общие сведения, 66.03kb.
- Shanti ananda maurice, 89.84kb.
- Методические материалы, 3002.45kb.
щить статистику процессов. Еще один пример: драйверы могут вести
трассировку записей в удобном для отладки виде, а драйвер трасси-
ровки дает возможность пользователям читать эти записи. Наконец,
профиль ядра, рассмотренный в главе 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, ни записывать файлы в файловые системы, смонтированные только
для чтения. Более того, так как каждый раздел (и, следовательно,
файловая система) занимает на диске смежные дорожки и цилиндры,
скопировать всю файловую систему легче, чем в том случае, если бы