Добавлен: 29.10.2018
Просмотров: 48142
Скачиваний: 190
5.3. Уровни программного обеспечения ввода-вывода
401
Когда происходит прерывание, то все необходимое для его обработки делает процеду-
ра — обработчик прерывания. Затем она может разблокировать ожидавший данного
прерывания драйвер. В ряде случаев она просто выполняет процедуру up на семафоре.
В других случаях вызывает процедуру signal для переменной условия в мониторе. Во
всех остальных случаях она посылает заблокированному драйверу сообщение. В любом
случае заключительным действием прерывания будет предоставление возможности
возобновления работы ранее заблокированному драйверу. Такая модель лучше всего
работает в том случае, если драйверы структурно относятся к процессам ядра, имея
собственные состояния, стеки и счетчики команд.
Разумеется, в действительности не все так просто. Обработка прерывания не ограни-
чивается только его перехватом, вызовом процедуры up для некого семафора, а затем
выполнением команды IRET для возвращения из прерывания к предыдущему процессу.
На операционную систему возлагается куда более существенный объем работы. Сейчас
будет дано лишь краткое описание этой работы в виде последовательности шагов, которые
должны быть выполнены программным обеспечением после завершения аппаратного пре-
рывания. Следует заметить, что подробности этой работы сильно зависят от конкретной
операционной системы, поэтому некоторые из перечисленных далее шагов конкретной
машине могут и не понадобиться, в отличие от шагов, не попавших в этот перечень. Кроме
того, реальные шаги на некоторых машинах могут следовать в ином порядке.
1. Сохранить все регистры (включая PSW), которые еще не были сохранены аппа-
ратурой, вызвавшей прерывание.
2. Установить контекст для процедуры обработки прерывания. Здесь может быть
задействована установка TLB, MMU и таблицы страниц.
3. Установить стек для процедуры обработки прерывания.
4. Послать подтверждение контроллеру прерываний. В отсутствие централизован-
ного контроллера прерываний разрешить прерывания.
5. Скопировать регистры из того места, где они были сохранены (возможно, из ка-
кого-нибудь стека), в таблицу процессов.
6. Запустить процедуру обработки прерывания, которая извлечет информацию из
регистров контроллера устройства, вызвавшего прерывание.
7. Выбрать следующий запускаемый процесс. Если прерывание привело к готовности
какого-то ранее заблокированного процесса, имеющего высокий уровень приори-
тета, то теперь может быть выбран запуск именно этого процесса.
8. Установить контекст MMU для следующего запускаемого процесса. Могут по-
требоваться и некоторые установки TLB.
9. Загрузить регистры нового процесса, включая его PSW.
10. Запустить выполнение нового процесса.
Из всего этого можно понять, что обработка прерываний — весьма непростой процесс.
К тому же она состоит из существенного количества команд центрального процессора,
особенно на машинах с виртуальной памятью, которым необходимы установка стра-
ничных таблиц или сохранение состояния MMU (например, битов R и M). На некото-
рых машинах при переключениях между пользовательским режимом и режимом ядра
необходимо также управлять TLB и кэшем центрального процессора, для чего также
нужны дополнительные машинные циклы.
402
Глава 5. Ввод и вывод информации
5.3.2. Драйверы устройств
Ранее в этой главе уже рассматривалась работа контроллеров устройств. Было пока-
зано, что у каждого контроллера есть ряд регистров устройства, предназначенных для
отправки ему команд, или ряд регистров устройства, используемых для считывания
его состояния, или и те и другие регистры. Число регистров устройства и характер ко-
манд очень сильно различаются в зависимости от конкретного устройства. Например,
драйвер мыши должен воспринимать информацию от мыши, сообщающую, насколько
далеко она была перемещена и какие кнопки в данный момент были отпущены. В отли-
чие от этого драйвер диска должен знать все о секторах, дорожках, цилиндрах, головках,
перемещениях блока головок, электроприводах, временных показателях стабилизации
головки и обо всех остальных механизмах, обеспечивающих нормальную работу диска.
Несомненно, эти драйверы будут сильно отличаться друг от друга.
Поэтому для управления каждым подключенным к компьютеру устройством ввода-вы-
вода требуется специальная программа, учитывающая его особенности. Эта программа
называется драйвером устройства. Обычно она создается производителем устройства
и поставляется вместе с этим устройством. Поскольку для каждой операционной си-
стемы нужны собственные драйверы, производитель устройства обычно поставляет
драйверы для нескольких наиболее популярных операционных систем.
Каждый драйвер устройства обычно управляет одним типом устройства или как мак-
симум одним классом родственных устройств. Например, драйвер SCSI-диска обычно
может управлять несколькими SCSI-дисками разного объема и разной скорости и,
может быть, приводом Blu-ray-диска со SCSI-интерфейсом. А вот мыши и джойстики
настолько отличаются друг от друга, что для них, как правило, требуются разные драй-
веры. Тем не менее технически вполне возможно создание одного драйвера устройства,
управляющего несколькими разнородными устройствами. Но это в большинстве слу-
чаев не самая лучшая идея.
Но иногда совершенно разные устройства основаны на одной и той же базовой техноло-
гии. Наверное, наиболее известным примером может послужить USB — технология по-
следовательной шины, которая не зря называется универсальной. К USB-устройствам
относятся диски, карты памяти, фотоаппараты, мыши, клавиатуры, мини-вентиляторы,
беспроводные сетевые карты, роботы, считыватели кредитных карт, аккумуляторные
бритвы, измельчители бумаг, сканеры штрих-кодов и портативные термометры. Все
они используют USB, хотя занимаются совершенно разными вещами. Характерная
особенность заключается в том, что из USB-драйверов обычно формируется стек, по-
добный TCP/IP-стеку в сетях. Внизу, как правило, в оборудовании, находится уровень
USB-ссылок (последовательного ввода-вывода), обрабатывающий все, что касается
аппаратуры, например сигнализацию и декодирование потоков сигналов в USB-пакеты.
Он используется для более высоких уровней, работающих с пакетами и функциями
USB, общими для большинства устройств. И наконец, наверху над всем этим находятся
высокоуровневые API-интерфейсы, например интерфейсы для накопителей, камер
и т. д. Таким образом, у нас по-прежнему имеются отдельные драйверы устройств, не-
смотря на то что ими совместно используются части стека протокола.
Чтобы получить доступ к аппаратной части устройства, то есть к регистрам контролле-
ра, драйвер устройства, как правило, должен быть частью ядра операционной системы,
по крайней мере в существующих на сегодняшний день архитектурах. Но вообще-то
можно создавать и драйверы, работающие в пространстве пользователя, используя
5.3. Уровни программного обеспечения ввода-вывода
403
при этом системные вызовы для чтения и записи регистров устройств. Такое решение
позволит изолировать ядро от драйверов и драйверы друг от друга, устранив при этом
основной источник системных сбоев — «сырые» драйверы, тем или иным образом
мешающие работе ядра. Несомненно, это хороший выход из положения при создании
высоконадежных систем. Примером системы, в которой драйверы устройств запуска-
ются в качестве процессов пользователя, может послужить MINIX 3 (
www.minix3.org
).
Но поскольку большинство других операционных систем для настольных компьютеров
предполагают запуск драйверов в ядре, рассматриваться будет именно такая модель.
Так как разработчики любых операционных систем знают, что эти фрагменты про-
граммного кода (драйверы), созданные другими разработчиками, будут устанавливать-
ся в их систему, им нужна такая архитектура, которая позволит подобную установку.
А это значит, что должна быть вполне определенная модель того, чем занимается
драйвер и как он взаимодействует со всей операционной системой. Как показано на
рис. 5.10, драйверы устройств обычно размещаются ниже остальных компонентов
операционной системы.
Рис. 5.10. Логическое позиционирование драйверов устройств. На самом деле обмен
информацией между драйверами и контроллерами устройств осуществляется по шине
Обычно операционная система относит драйверы к одной из немногочисленных кате-
горий. Самые распространенные категории — это драйверы блочных устройств, к ним
относятся драйверы дисков, содержащих множество блоков данных, к которым можно
404
Глава 5. Ввод и вывод информации
обращаться независимо от всех остальных блоков, и драйверы символьных устройств,
к которым относятся драйверы клавиатур и принтеров — устройств, которые генери-
руют или воспринимают поток символов.
Для многих операционных систем определены стандартный интерфейс, который дол-
жен поддерживаться всеми драйверами блочных устройств, и еще один стандартный
интерфейс, который должен поддерживаться всеми драйверами символьных устройств.
Эти интерфейсы состоят из нескольких процедур, которые могут вызываться всей
остальной операционной системой для обращения к драйверу. К этим процедурам
относятся, например, процедуры чтения блока (в блочных устройствах) или записи
строки символов (в символьных устройствах).
В некоторых системах операционная система представляет собой единую программу
в двоичных кодах, в которой содержатся все необходимые ей скомпилированные драй-
веры. Такая схема долгие годы была нормой для систем семейства UNIX, поскольку
они работали в компьютерных центрах, где устройства ввода-вывода менялись очень
редко. При добавлении нового устройства системный администратор просто переком-
пилировал ядро с новым драйвером для создания нового двоичного кода.
С наступлением эры персональных компьютеров с несметным количеством устройств
ввода-вывода эта модель уже не работает. Лишь немногие пользователи способны
перекомпилировать или перекомпоновать ядро, даже если у них будут исходные коды
или объектные модули, что случается довольно редко. Вместо этого операционные си-
стемы, начиная с MS-DOS, перешли к модели, в которой драйверы стали динамически
загружаться в систему в процессе работы. Управление загрузкой драйверов ведется
в разных системах по-разному.
На драйвер устройства возлагается несколько функций. Наиболее очевидные из них —
восприятие абстрактных запросов на чтение и запись от независимого от конкретных
устройств программного обеспечения, находящегося выше них по уровню, и отсле-
живание порядка их выполнения. Но на них возлагается также ряд других функций.
Например, драйвер должен при необходимости инициализировать устройство. Он
может понадобиться также для управления энергопотреблением устройства и реги-
страции событий.
Многие драйверы устройств имеют сходную общую структуру. Типичный драйвер
начинает свою работу с проверки приемлемости входных параметров. Если они не-
приемлемы, возвращается сообщение об ошибке. Если с параметрами все в порядке,
может понадобиться перевод абстрактных понятий в конкретные. Для драйвера диска
это может означать преобразование обычного номера блока в номера головки, дорожки,
сектора и цилиндра, относящихся к геометрии диска.
Затем драйвер может проверить, используется ли устройство в данный момент. Если
оно используется, запрос будет поставлен в очередь для последующей обработки.
Если устройство простаивает, проверяется состояние аппаратуры, чтобы определить,
может ли запрос быть обработан. Перед началом передачи данных может понадобиться
включить устройство или запустить его двигатель. Как только устройство включится
и будет готово к работе, им можно будет управлять.
Управление устройством означает выдачу в его адрес последовательности команд.
Именно драйвер определяет последовательность команд в зависимости от того, что
должно быть сделано. После того как драйвер поймет, какие команды он собирается вы-
дать, он начнет записывать их в регистры контроллера устройства. После записи каж-
5.3. Уровни программного обеспечения ввода-вывода
405
дой команды в контроллер может потребоваться проверка того, принял ли контроллер
команду и готов ли к приему следующей команды. Эта последовательность повторяется
до тех пор, пока не будут выданы все команды. Некоторым контроллерам можно указы-
вать на связанный список команд (в памяти) и предписывать самостоятельное чтение
и обработку этих команд без дальнейшей помощи со стороны операционной системы.
После того как команды были выданы, может сложиться одна из двух ситуаций.
В большинстве случаев драйвер должен ждать, пока контроллер не сделает в его инте-
ресах какую-нибудь работу, поэтому он самоблокируется до тех пор, пока не поступит
прерывание на его разблокировку. Но в других случаях операция завершается без за-
держки и драйверу не нужно блокироваться. В качестве примера последней ситуации
можно привести прокрутку экрана в символьном режиме, требующую лишь записи
нескольких байтов в регистры контроллера. Для этого не нужно никаких механических
перемещений, поэтому вся операция может быть завершена за несколько наносекунд.
В первом случае заблокированный драйвер будет активизирован прерыванием. Во вто-
ром случае он никогда не будет переходить в неактивное состояние. В любом случае по
завершении операции драйвер должен провести проверку на отсутствие ошибок. Если
все в порядке, драйвер может получить данные (например, только что считанный блок)
для передачи программному обеспечению, не зависящему от применяемого устрой-
ства. И наконец, он возвращает вызывавшей его программе определенную информа-
цию о состоянии устройства, наличии или отсутствии ошибок. Если в очереди были
какие-нибудь другие запросы, то теперь один из них может быть выбран и запущен
на выполнение. Если запросов в очереди не было, драйвер блокируется в ожидании
следующего запроса.
Эта упрощенная модель дает весьма приблизительное понятие о реальной работе. На
самом деле программный код из-за множества различных факторов куда сложнее. Пре-
жде всего, устройство ввода-вывода может завершить свою работу и прервать работу
драйвера. Прерывание может стать причиной запуска драйвера. Между прочим, оно
может вызвать запуск текущего драйвера. К примеру, при обработке сетевым драйвером
входящего пакета может прибыть еще один пакет. Следовательно, драйверы должны
быть реентерабельными, то есть работающий драйвер еще до завершения первого вы-
зова должен ожидать повторного вызова.
В системе, допускающей горячее подключение, устройство может быть добавлено или
удалено во время работы компьютера. В результате, пока драйвер занят чтением с како-
го-нибудь устройства, система может ему сообщить, что пользователь внезапно удалил
это устройство из системы. Драйверу придется не только прервать текущую передачу
данных, не повредив при этом каких-либо структур данных ядра, но и умудриться
элегантно удалить из системы все отложенные запросы для только что удаленного
устройства и сообщить плохие новости пославшим их программам. Более того, неожи-
данное добавление новых устройств может заставить ядро перераспределить ресурсы
(например, линии запроса прерываний), забирая у драйвера старые и предоставляя
вместо них новые.
Драйверам не разрешается делать системные вызовы, но часто возникает необходи-
мость взаимодействовать с остальной частью ядра. Обычно им разрешается вызывать
определенные процедуры ядра. Например, для использования в качестве буферов
выделенных аппаратуре страниц памяти драйверы вызывают процедуры, которые
занимаются их выделением и освобождением. Другие полезные вызовы нужны для
управления MMU, таймерами, контроллером DMA, контроллером прерываний и т. д.