Устройства и драйверы

Как говорилось в прошлой лекции, процессор взаимодействует с внешними устройствами через шины. Любое устройство, подключенное к системной шине (или к шине PCI, или USB), обладает определённым интерфейсом (заранее известными идентификаторами, номерами портов ввода и вывода и т. п.), через который производится обмен данными и сообщениями с устройством. Таким образом, в первую очередь операционная система должна иметь модули, осуществляющее управление шинами, прерываниями, DMA и т. п. Эти модули обычно загружаются при старте системы.

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

  • символьные — чтение и запись данных производится побайтно (например, COM-порт);
  • блочные — чтение и запись данных производится блоками (например, секторами в жётском диске);
  • последовательного доступа — читать и записывать данные можно только последовательно (например, коммуникационные каналы);
  • произвольного доступа — возможно обращение к данным по индексу (диски);
  • синхронные — передают данные только по команде (например, принтер);
  • асинхронные — могут передавать данные без предварительного указания (например, сетевая карта).

Для работы с каждым из обозначенных видов устройств нужна сответствующая подпрограмма в ядре — только она знает, каким образом пользовательские данные конвертируются в сигналы шины и наоборот. Такая подпрограмма ядра (оформляемая обычно в виде модуля) называется драйвером. В UNIX можно выделить несколько групп устройств (и соответствующих драйверов), основные из них: блочные устройства (блочные, произвольного доступа), символьные устройства (символьные, последовательного доступа), сетевые устройства (блочные, последовательного доступа, асинхронные). Интересная особенность блочных утройств состоит в том, что при обращении к ним может использоваться буфер, в котором кэшируются данные последних запросов. Сетевые устройства представлены в системе в виде сетевых интерфейсов.

В операционной системе UNIX большинство внешних устройств доступно пользовательским программам в виде специальных файлов. Эти файлы могут быть, соответственно, двух типов — символьные и блочные. Традиционно, все файлы устройств располагаются в каталоге /dev и имеют имена, соответствующие назначению устройства. Например, в операционной системе Linux устройства терминала обозначаются как /dev/tty0, /dev/tty2 и т. д. с увеличением порядкового номера, жёсткий диск — /dev/sda, а порт PS/2 — /dev/psaux. Названия однотипных устройств обычно отличаются целым числом. Программы могут открывать, читать и записывать данные в файлы устройств как в обычные файлы, при этом операционная система транслирует пользовательские запросы драйверу соответствующего устройства.

Рисунок 1.21. Взаимодействие с устройством через файл

 

Таким образом, файл устройства является одним из интерфейсов взаимодействия процессов с устройствами. Каждый файл устройства имеет два номера — старший и младший. По этим номерам операционная система определяет, какой драйвер должен использоваться при обращении к этому файлу.

В операционной системе UNIX большая часть драйверов устройств выполнена в виде модулей ядра. Однако, некоторые драйверы частично выполняются в пользовательском режиме (например, видеокарты в рамках графической подсистемы X Windows).

Помимо файлов, соответствующих внешним устройствам, в UNIX есть несколько стандартных файлов виртуальных устройств. Эти файлы могут передавать и принимать от пользовательских процессов специальные данные, например, из символьного устройства /dev/zero можно прочитать только нули, сколько бы процесс не читал данные из этого файла. Вот список наиболее распространённых виртуальных устройств:

/dev/console

устройство соответствует активной в данный момент терминальной линии (виртуальной консоли);

/dev/null

«чёрная дыра» — любая информация, записанная в этот файл, пропадает безвозвратно, обычно используется для поглощения ненужного вывода программ;

/dev/random и /dev/urandom

устройства, генерирующие соответственно случайные и псевдослучайные данные;

/dev/stdin, /dev/stdout и /dev/stderr

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

/dev/zero

устройство генерирует нули — из этого устройства можно прочитать сколько угодно нулевых байт.