Функции драйверов
Речь идет об отображении в память данных, хранящихся на устройстве. Для устройств ввода-вывода, например, для принтера или терминала, эту функцию невозможно реализовать разумным образом. Напротив, для лент и других последовательных устройств памяти, поддерживающих функцию Iseek, отображение может быть реализовано с использованием аппаратных средств виртуализации памяти и операции read и write. Необходимость специальной функции отображения появляется у драйверов устройств, использующих большие объемы памяти, отображенной в адресное пространство системной шины, например, для растровых видеоадаптеров, некоторых звуковых устройств или страниц общей памяти (backpane memory – двухпортовой памяти, используемой как высокоскоростной канал обмена данными в многопроцессорных системах).
Механизм отображения доступных прикладной программе системных вызовов в функции драйвера относительно сложен. Этот механизм должен включать в себя следующее.
- Изменение способа идентификации устройства. "Ручка" представляет собой специфичный для пользовательского процесса номер, в то время как к драйверу могут обращаться разные процессы. В системах семейства Unix для идентификации устройства используется упомянутая выше минорная запись, которая должна содержать указатель на блок переменных состояния устройства.
- Передачу или отображение данных из пользовательского адресного пространства в системное и обратно.
- Взаимодействие потоков пользовательского процесса (а в общем случае – нескольких пользовательских процессов, одновременно использующих устройство) с потоками драйвера.
Способы, которыми эти вопросы решаются в современных операционных системах, обсуждаются в последующих разделах. А пока что мы подробнее обсудим, какие именно операции над устройством следует определить и почему.
Видно, что предлагаемый системами семейства Unix набор операций рассматривает устройство как неструктурированный поток байтов (или, для устройств ввода-вывода, два разнонаправленных потока – для ввода и для вывода). Такое рассмотрение естественно для устройств алфавитно-цифрового ввода-вывода и простых запоминающих устройств, например магнитных лент, однако далеко не столь естественно для более сложных устройств.
Стандартный ответ Unix-культуры в этом случае таков: любая, сколь угодно сложная структура данных может быть сериализована – преобразована в последовательный поток байтов. Например, изображение может быть превращено в последовательный поток байтов в виде растровой битовой карты или последовательности описаний графических примитивов – линий, прямоугольников и пр. Примерами такой сериализации для изображений могут являться язык PostScript [partners.adobe.com] и протокол распределенной оконной системы X Window [www.x.org] (оба протокола поддерживают как растровые образы, так и довольно богатые наборы векторных примитивов).
Нередки, впрочем, ситуации, когда нам интересна не только структура поступающих данных, но и время их поступления (в предыдущей главе мы предложили классифицировать устройства, которые могут быть использованы подобным образом, как генераторы событий) – это бывает в приложениях реального времени, а также в задачах, которые сейчас стало модно называть "задачами мягкого реального времени" – мультимедийных программах, генерирующих поток звука, синхронизованного с изображением, и, особенно, в компьютерных играх.
Для работы с таким устройством прикладная программа, так или иначе, должна зарегистрировать обработчик поступающих от устройства событии. В системах Unix такая регистрация состоит в открытии устройства для чтения, а ожидание события заключается в выполнении над этим устройством операции чтения. Для последовательных устройств ввода операция чтений разблокируется, когда с устройства поступят хоть какие-то данные (а не тогда, когда будет заполнен весь буфер), поэтому, если пришло только одно событие, мы его не пропустим. Драйверы многих устройств, способных работать в качестве генераторов событий, имеют команды ioctl, позволяющие более тонко управлять условием разблокирования функции read.
Для того чтобы во время ожидания события от генератора, заниматься еще какой-то полезной работой, предлагается либо выделить ожидающий события вызов read в отдельную нить, либо пользоваться системными вызовами lect и poll, позволяющими ожидать событий на нескольких устройствах (а также средствах межпроцессного взаимодействия) одновременно.
Другие ОС предоставляют для работы с устройствами-генераторами событий более сложные механизмы, зачастую основанные на callback (дословно – "вызов назад"; механизм взаимодействия подсистем, когда подсистема, запрашивающая сервис, передает обслуживающей подсистеме указатель на функцию, которую необходимо вызвать при наступлении определенного события).
Работа с генераторами событий требует решения еще одной задачи – хранения поступающих событий в периоды, когда пользовательская программа их не успевает обрабатывать. Необходимость относительно сложных схем работы с требуемыми для этого буферами вынудила разработчиков Unix System V Release 3 ввести еще один тип драйверов – потоковые (STREAMS) [docs.sun.com 805-7478-10]. Для прикладной программы потоковый драйвер не отличается от обычного символьного устройства, но отличий с точки зрения системы довольно много. Некоторые из этих отличий будут рассматриваться далее.
Unix System V Release 3 (SCO Open Desktop, SCO OpenServer), Release (SCO UnixWare, SGI Irix, Sun Solaris) и системы, испытавшие влияние OSF Unix (IBM AIX, HP/UX) используют потоковые драйверы для реализации таких важных псевдоустройств, как трубы и сокеты TCP/IP. Кроме того, потоковыми в этих системах являются драйверы сетевых адаптеров и терминальных устройств.
С другой стороны, в OS/2 и Windows NT/2000/XP существуют обширные номенклатуры типов драйверов с различными наборами функций. Так, в OS/2 используются драйверы физических устройств следующих типов:
- простые драйверы последовательных устройств ввода-вывода, аналогичные драйверам символьных устройств в Unix;
- Драйверы запоминающих устройств прямого доступа, аналогичные драйверам блочных устройств в Unix;
- Драйверы видеоадаптеров, используемые графической оконной системой Presentation Manager (PM);
- Драйверы позиционных устройств ввода (мышей и др.), также используемые РМ;
- Драйверы принтеров и других устройств вывода твердой копии;
- Драйверы звуковых устройств, используемые подсистемой "мультимедиа" MMOS/2;
- драйверы сетевых адаптеров стандарта NDIS, используемые сетевым программным обеспечением фирм IBM и Microsoft;
- драйверы сетевых адаптеров стандарта ODI, используемые программным обеспечением фирмы Novell;
- DMD (Device Manager Driver – драйвер-менеджер класса устройств (в разд. 10.2 мы подробнее разберемся с назначением драйверов этого типа);
- различного рода "фильтры", например, ODINSUP.SYS – преобразователи ODI-интерфейса в NDIS.