Драйверная модель wdm
В продолжение предыдущего раздела хотелось бы отметить, что в конечном итоге, компания Microsoft разработала новую технологию — WDM (Windows Driver Model) — и включила ее в Windows 98 и Windows Me, наследников Windows 95. Кроме того, новая технология была включена в Windows 2000 и Windows ХР, наследников Windows NT 4.0 [7]. WDM предоставляла разработчикам драйверов доступ к практически полному диапазону технологий и интерфейсов, доступных для мира ядра Windows.
Вот теперь мы уже заговорили о таком понятии, как «драйверная модель» напрямую. Возможно, кому-то будет интересно узнать, что же это такое. Так вот, драйверная модель предоставляет собой абстрактный интерфейс для поддержки устройств. Задача разработчиков драйверов как раз и состоит в реализации данного интерфейса, чтобы поддерживать конкретные требования аппаратного устройства. Выражаясь более простым языком, задачей драйвера является предоставление связи и контролирование взаимодействия между приложениями и устройством.
Я думаю, нет необходимости в подробностях объяснять, что представляет собой драйвер, как он «пишется», как выполняется под управлением операционной системы и проч., ибо это тема заслуживает отдельного доклада. Скажу лишь то, что драйвер представляет собой «контейнер» для функций, которые самостоятельно вызываются операционной системой в нужный момент (когда она сочтет нужным). Да-да, здесь нет ничего мистического. Другое дело, что драйверы обычно выполняются в доверенном пространстве ядра, хотя, в драйверной модели WDF драйверы UMDF выполняются в пользовательском режиме, но об этом мы еще поговорим.
Архитектура wdm
Ни для кого не секрет, что тема драйверов достаточно объемная и сложная, поэтому мы не будем уходить в «дебри» системного низкоуровнего программирования, а рассмотрим лишь в общих чертах особенности драйверных моделей. А для большей наглядности, рассмотрим архитектуру WDM на небольшом примере.
Допустим, программе пользовательского режима потребовалось прочитать данные с устройства. Она вызывает функцию API ReadFile, к примеру. Модуль подсистемы - такой как KERNEL32.DLL - реализует вызов, передавая его к низкоуровневой функции API NtReadFile [7]. Низкоуровневый API – довольно важная составляющая WDM да и архитектуры Windows в целом, но о нем чуть позже.
Такие функции, как, например, NtReadFile, является частью системного компонента, называемого менеджером ввода/вывода (I/O Manager). Следует иметь ввиду, что в самой системе не существует конкретного исполняемого модуля с таким именем. Тем не менее, нам потребуется какое-то название для обозначения «облака» функций операционной системы, окружающего драйвер.
В рассматриваемом примере с ReadFile функция NtReadFile создает IRP с основным кодом функции IRP_MJ_READ. Дальнейшее может различаться в деталях, но чаще всего такие функции, как NtReadFile, возвращают управление вызывающей стороне пользовательского режима [7].
А теперь немного поводу низкоуровнего API. Низкоуровневый API представляет интерфейс, который позволяет «клиентским» приложениям обращаться к программам (в данном контексте - драйверам) режима ядра. Дело в том, что каждая точка входа данного API является тонкой оберткой для функции режима ядра. Все они проверяют свои параметры, предотвращая возможные дефекты безопасности. Сам вызов использует платформенно-зависимый интерфейс для передачи управления через границу пользовательского режима/режима ядра (например, инструкции ассемблера и т.п.). Функции низкоуровнего API работают в режиме ядра и обслуживают запросы приложений на обращение к устройству. Как правило, они создают структуру данных, называемую пакетом запроса ввода-вывода, или IRP (I/O Request Packet), и передают ее в точке входа некоторому драйверу устройства.
Для выполнения запроса IRP драйверу, в конечном счете, потребуется обратиться к своему устройству. Драйверы, хотя они и работают в режиме ядра и могут напрямую общаться с оборудованием, используют средства прослойки HAL (Hardware Abstraction Layer) для обращения к оборудованию. Функции HAL используют платформенно-зависимую реализацию для выполнения операций. Закончив операцию ввода/вывода, драйвер завершает обработку IRP, вызывая соответствующую функцию режима ядра. Завершение является последним этапом обработки IRP и позволяет ожидающему приложению продолжить работу. Для большей наглядности рекомендую обратиться к рисунку 1.
Рисунок 1 – Принцип работы драйверов WDM
Вот, собственно, по такому принципу и работают драйверы WDM. Конечно, то, что мы рассмотрели, это, примерно, сотая часть архитектуры WDM и всего того, что с ней связано. Однако, в рамках статьи, надеюсь, вполне достаточно.
Итак, вот несколько ключевых особенностей драйверной модели WDM (по сравнению с предыдущей драйверной моделью – «драйвер виртуального устройства», применяемой в Windows 9x/ME)[2, 3, 5]:
Совместимость на уровне двоичных кодов между драйверами для Windows 98 и NТ;
Поддержка управления питанием (power management), что дает системе возможность осуществления энергосбережения путем выборочного отключения питания нескольких или всех устройств в системе;
Поддержка Plug and Play;
Поддержка "продвинутого" шинного управления (advanced bus management);
Единая обработка операций ввода/вывода посредством общей структуры данных - IRP (в Windows 98/Ме существовали серьезные различия в работе с дисками, коммуникационными портами, клавиатурами и т. д.).