Драйверы внешних устройств
|
Когда я на почте служил ямщиком .
Ко мне постучался косматый геолог,
И глядя на карту на белой стене,
он усмехнулся мне.
Г. Самойлов |
Драйвер (driver) представляет
собой специализированный программный модуль, управляющий внешним устройством.
Слово driver происходит от глагола to drive (вести) и переводится с английского
языка как извозчик или шофер: тот, кто ведет транспортное средство. Драйверы
обеспечивают единый интерфейс для доступа к различным устройствам, тем
самым устраняя зависимость пользовательских программ и ядра ОС от особенностей
аппаратуры.
Драйвер не обязательно должен управлять каким-либо физическим устройством.
Многие ОС предоставляют также драйверы виртуальных устройств или псевдоустройств
— объектов, которые ведут себя аналогично устройству ввода-вывода, но
не соответствуют никакому физическому устройству.
В виде псевдоустройств реализуются трубы в системах семейства Unix и почтовые
ящики в VMS. Еще одним примером полезного псевдоустройства являются устройства
/dev/null в Unix и аналогичное ему \DEV\NUL в MS DOS\Windows\OS/2. В современных
системах семейства Unix в виде псевдоустройств, размещенных в псевдофайловой
системе /ргос, реализован доступ к большинству параметров системы -- адресным
пространствам активных процессов, статистике и параметрам настройки ядра,
данным отдельных подсистем, например таблице маршрутизации сетевого протокола
IP.
Прикладные программы, использующие собственные драйверы, не так уж редки
- примерами таких программ могут быть GhostScript (свободно распространяемый
интерпретатор языка PostScript, способный выводить программы на этом языке
на различные устройства, как печатающие, так и экранные) или LATEX,
который также способен печатать на самых разнообразных устройствах. Однако
эта глава посвящена преимущественно драйверам, используемым ядром ОС.
Большинство ОС общего назначения запрещают пользовательским программам
непосредственный доступ к аппаратуре. Это делается для повышения надежности
и обеспечения безопасности в многопользовательских системах. В таких системах
драйверы являются для прикладных программ единственным способом доступа
к внешнему миру.
Еще одна важная функция драйвера- это взаимоисключение доступа к устройству
в средах с вытесняющей многозадачностью. Допускать одновременный неконтролируемый
доступ к устройству нескольких параллельно исполняющихся процессов просто
нельзя, потому что для большинства внешних устройств даже простейшие операции
ввода-вывода не являются атомарными.
Например, в большинстве аппаратных реализаций последовательного порта
RS232 передача байта состоит из четырех шагов: записи значения в регистр
данных, записи команды "передавать" в регистр команды, ожидания
прерывания по концу передачи и проверки успешности передачи путем считывания
статусного регистра устройства. Нарушение последовательности шагов может
приводить к неприятным последствиям — например, перезапись регистра данных
после подачи команды, но до завершения передачи, может привести к остановке
передачи или, что еще хуже, передаче искаженных данных и т. д.
Нельзя также забывать о неприятностях более высокого уровня — например,
смешивании вывода разных процессов на печати или данных — на устройстве
внешней памяти. Поэтому оказывается необходимо связать с каждым внешним
устройством какой-то разграничитель доступа во времени. В современных
ОС эта функция возлагается именно на драйвер. Обычно одна из нитей драйвера
представляет собой процесс-монитор, выполняющий асинхронно поступающие
запросы на доступ к устройству. В Unix, OS/2 и Windows NT/2000/XP этот
процесс называется стратегической функцией. Подробнее этот механизм обсуждается
в разд. Асинхронный ввод-вывод и Асинхронная
модель ввода-вывода с точки зрения приложений.
При определении интерфейса драйвера разработчики ОС должны найти правильный
баланс между противоречивыми требованиями:
- стремлением как можно сильнее упростить драйвер, чтобы
облегчить его разработку и (косвенно) уменьшить вероятность опасных
ошибок;
- желанием предоставить гибкий и интеллектуальный интерфейс
к разнообразным устройствам.
Драйверы обычно разрабатываются не поставщиками операционной
системы, а сторонними фирмами — разработчиками и изготовителями периферийного
оборудования. Поэтому интерфейс драйвера является ничуть не менее внешним,
чем то, что обычно считается внешним интерфейсом ОС — интерфейс системных
вызовов. Соответственно, к нему предъявляются те же требования, что и
к любому другому внешнему интерфейсу: он должен быт умопостижимым, исчерпывающе
документированным и стабильным — меняться непредсказуемо от одной версии
ОС к другой. Идеальным варпан том была бы полная совместимость драйверов
хотя бы снизу вверх, чтобы драйвер предыдущей версии ОС мог использоваться
со всеми последующими версиями.
Потеря совместимости в данном случае означает, что все независимые изготовители
оборудования должны будут обновить свои драйверы. Организация такого обновления
оказывается сложной, неблагодарной и часто попросту невыполнимой задачей
— например, потому, что изготовитель оборудования уже не существует как
организация или отказачся от поддержки данного устройства.
Отказ от совместимости драйверов на практике означает "брошенное"
периферийное оборудование и, как следствие, "брошенных" пользователей,
которые оказываются вынуждены либо отказываться от установки новой системы,
либо заменять оборудование. Оба варианта, естественно, не улучшают отношения
пользователей к поставщику ОС, поэтому многие поставщики просто не могут
позволить себе переделку подсистемы ввода-вывода. Таким образом, интерфейс
драйвера часто оказывается наиболее консервативной частью ОС.
Подсистема ввода-вывода OS/2
В качестве примера такого консерватизма можно привести подсистему ввода-вывода
OS/2. Совместный проект фирм IBM и Microsoft, OS/2 1.x разрабатывалась
как операционная система для семейства персональных компьютеров Personal
System/2. Младшие модели семейства были основаны на 16-разрядном процессоре
80286, поэтому вся ОС была полностью 16-битной.
Позднее разработчики фирмы IBM реализовали 32-битную OS/2 2.0, но для
совместимости со старыми драйверами им пришлось сохранить 16-битную подсистему
ввода-вывода. Все точки входа драйверов должны находиться в 16-битных
('USE16') сегментах кода; драйверам передаются только 16-разрядные ('far')
указатели и т. д.
По утверждению фирмы IBM, они рассматривали возможность реализации также
и 32-битных драйверов, но их измерения не показали значительного повышения
производительности при переходе к 32-битной модели. Позднее, впрочем,
суперскалярные процессоры архитектуры х86, оптимизированные Дл^ исполнения
32-разрядного кода, все-таки продемонстрировали значительный рост производительности
для такого кода по сравнению с 16-разрядным, и, начиная с OS/2 4.0 32-битный
интерфейс все-таки предоставляется, но без отказа от поддержки старых,
16-разрядных драйверов.
Благодаря этому сохраняется возможность использовать драйверы, разработанные
еще в конце 80-х и рассчитанные на OS/2 1.x. Эта возможность оказывается
особенно полезна при работе со старым оборудованием.
Напротив, разработчики фирмы Microsoft отказались от совместимости с 16-битными
драйверами OS/2 1.x в создававшейся ими 32-битной версии OS/2 называвшейся
OS/2 New Technology. Фольклор утверждает, что именно это техническое решение
оказалось причиной разрыва партнерских отношении между Microsoft и IBM,
в результате которого OS/2 NT вышла на рынок под названием Windows NT
3.1.
Подсистема ввода-вывода Windows 9x/ME
Сама фирма Microsoft, впрочем, демонстрирует почти столь же трогательную
приверженность к совместимости со старыми драйверами: системы линии Windows
95/98/ME до сих пор используют весьма своеобразную архитектуру, основной
смысл которой — возможность применять, пусть и с ограничениями, драйверы
MS DOS.
Windows 9х и Windows 3.x в enhanced-режиме предоставляют вытесняющую многозадачность
для VDM (Virtual DOS Machine— Виртуальная машина [для] DOS), однако сами
используют DOS для обращения к дискам и дискетам. Ядро однозадачной DOS
не умеет отдавать управление другим процессам во время исполнения запросов
ввода-вывода. В результате во время обращения к диску все остальные задачи
оказываются заблокированы.
У современных PC время исполнения операций над жестким диском измеряется
десятыми долями секунды, поэтому фоновые обращения к жесткому диску почти
не приводят к нарушениям работы остальных программ. Однако скорость работы
гибких дисков осталась достаточно низкой, следовательно, работа с ними
в фоновом режиме блокирует систему на очень заметные промежутки времени.
Эффектная и убедительная демонстрация этой проблемы очень проста: достаточно
запустить в фоновом режиме форматирование дискеты или просто команду COPY
С:\ТМР\*.* А: , если в каталоге C:\TMP достаточно много данных.
При этом работать с системой будет практически невозможно: во время обращений
к дискете даже мышиный курсор не будет отслеживать движений мыши, будут
теряться нажатия на клавиши и т. д.
Windows 95/98/ME использует несколько методов обхода DOS при обращениях
к диску, поэтому пользователи этой системы не всегда сталкиваются с описанной
проблемой. Однако при использовании блочных драйверов реального режима
система по-прежнему применяет DOS в качестве подсистемы ввода-вывода,
и работа с дискетами в фоновых задачах также нарушает работу задач первого
плана.
Если говорить о совместимости, то со многих точек зрения
очень привлекательной представляется идея универсального драйвера — модуля,
который без изменении или с минимальными изменениями может использоваться
Для управления одним и тем же устройством в различных ОС.
К сожалению — несмотря даже на то, что, как мы увидим далее, в общих чертах
архитектура драйвера в большинстве современных ОС удивительн похожа —
идея эта, по-видимому, нереализуема. Даже для близкородственных ОС — например,
систем семейства Unix — драйверы одного и того ж устройства не всегда
могут быть легко перенесены из одной ОС в другую говоря уж о возможности
использования без модификаций. Еще более удивительным является тот факт,
что две линии ОС - Windows 95/98/MЕ и Windows NT/2000/XP — поставляемых
одной и той же компанией Microsoft и реализующих почти один и тот же интерфейс
системных вызовов — Win32 — имеют совсем разный интерфейс драйвера.
Проблема здесь в том, что интерфейс между драйвером и ядром ОС всегда
двусторонний: не только прикладные программы и ядро вызывают функции драйвера,
но и, наоборот, драйвер должен вызывать функции ядра. Структура интерфейсов
ядра, доступных драйверу, определяет многие аспекты архитектуры ОС в целом.
В предыдущих главах мы уже обсуждали многие из ключевых вопросов: способ
сборки ядра, стратегию управления памятью, способы обмена данными между
ядром и пользовательскими процессами, и, наконец, механизмы межпоточного
взаимодействия — между нитями самого драйвера (ниже мы увидим, что подавляющее
большинство драйверов состоит как минимум из двух нитей), между драйвером
и остальными нитями ядра и между драйвером и нитями пользовательских процессов.
Изложение решений перечисленных проблем составляет если и не полное описание
архитектуры ОС, то, во всяком случае, значительную его часть. Так, переход
от однозадачной системы или кооперативной многозадачности к вытесняющей
многозадачности может потребовать не только изменения планировщика, но
и радикальной переделки всей подсистемы ввода-вывода, в том числе и самих
драйверов.
Таким образом, до тех пор, пока используются ОС различной архитектуры,
разработка универсального интерфейса драйвера, если теоретически п возможна,
то практически вряд ли осуществима.
|