Использование NuMega DriverStudio для написания WDM-драйверов
Драйвер — системная программа, предназначенная для управления каким-либо физическим или виртуальным устройством компьютера.
Драйверы устройств, как правило, – наиболее критичная часть программного обеспечения компьютеров. По иронии судьбы, это также и наиболее скрытая часть системы и программного обеспечения. Драйверы устройств системы Windows фирмы Microsoft не являются исключением. Наоборот, если в UNIX можно взять исходники ядра и помотреть, как там пишутся драйвера, то в Windows это вряд ли будет возможным.
Вспомним первые персоналки и MS DOS, бывшую в то время практически единственным выбором для настольного ПК. Несмотря на всю ее просто ту, драйвера, конечно, присутствовали и в ней. Практически все дело ограничивалось накoпителями — дисководами, CD-ROM приводами, винчестерами, да элементарнейшими драйверами клавиатуры и дисплея. Для каждой программы, требующей большего, чем перечисленный набор оборудования, требовалось создавать собственный драйвер. Представьте себе, что вам требуется воспроизвести звук на имеющейся в компьютере звуковой карте. Если вы знаете ее модель и у вас есть хорошая документация, вы, потратив немало времени, напишете прoграмму, которая сделает все желаемое. По крайней мере, так утверждает идеология открытых систем в общем и Linux в частности. А если необходимо поддерживать две модели? Три? Двадцать? И это при учете того, что новая звуковая карта появляется не реже раза в полтора-два месяца? И все они могут быть подключены различными способами и общаться с компьютером через разнообразные шины? Естественный выход — возложить написание кода, специфичного для аппаратуры, на ее создателя. Да и фирма-производитель, наняв высококвалифицированных специалистов, справится с задачей намного эффективнее и быстрее. Во всех современных операционных системах так и поступают. Существуют требования, например, к драйверу звуковой карты, и пользователь устанавливает тот вариант, который соответствует его "железу". А программа-проигрыватель через вызовы системных функций указывает, что именно она хотела бы воспроизвести, не заботясь об особенностях аппаратуры.
Данный труд не является руководством по написанию драйверов устройств под Windows — для этого пришлось бы писать пару толстых книжек. В нем рассматриваются вопросы написания простого драйвера PCI-устройства под Win 98/ME/2000 с использованием архитектуры драйверов WDM и пакета NuMega DriverStudio. В дальнейшем, будем считать, что нашей целью будет драйвер, работающий на компьютере с архитектурой ЦП i386 под управлением вышеупомянутых ОС. Наше гипотетическое устройство — это PCI–карточка, имеющая некоторое количество памяти.
Прилагаемые к руководству исходные тексты драйвера и программ работы с ним были проверены на PCI-карточке XDSP680c.
Руководство изначально писалось как методические указания для курса "Специальные компьютерные системы" специальности "Компьютерные системы и сети" Черниговского государственного технологического университета, но затем было переработано и дополнено. Предполагается, что читатель знаком с основами программирования под Win32 и средой Visual C++.
1. Общие сведения о драйверах устройств в системе Windows.
Естественно, каждая операционная система имеет собственную архитектуру и свои особенности функционирования драйверов. Но практически во всех современных ОС можно выделить следующие особенности, характерные для работы подсистемы ввода-вывода:
• Фактически, пользовательские программы либо системные утилиты не могут напрямую обращаться к аппаратуре, используя порты ввода-вывода, DMA либо подобные низкоуровневые механизмы напрямую. Этот факт следует из самой идеологии защищенного режима современных ОС: все программы пользователя и часть ОС работают в 3-м кольце защиты компьютера (наименее привилегированном). При этом любая команда обращения к порту из данной программы может быть замаскирована и повлечет за собой аппаратное исключение (Exception). Напрямую к аппаратуре может обратится программа, работающая в самом приоритетном, 0-м кольце защиты.
• В настоящее время практически все устройства используют технологию автоматического распределения ресурсов (портов ввода-вывода, запросов на прерывания и т.п.) — Plug and Play (PnP). Когда новое устройство, например, та же звуковая карта, будет добавлена в систему, ей будут выделены те ресурсы, которые в данный момент свободны — незадействованные линии запросов на прерывание (IRQ), свободные адреса портов ввода-вывода. Поэтому драйвер изначально "не знает", какие именно адреса портов и IRQ ему будут выделены — эти данные будут различными для разных компьютеров. При этом задача распределения ресурсов ложится на ОС.
В ОС Windows, как и в большинстве современных ОС, драйвера управляют буквально всем: работой с аппаратурой, поддержкой файловых систем различных типов, сетевых протоколов и т.п. Это дает определенные преимущества и делает систему более гибкой: например, для того, чтобы ОС стала "понимать" другой сетевой протокол, нужно всего лишь установить соответствующий драйвер. 1.1 Система ввода-вывода в Windows.
На данный момент наиболее распространены два семейства ОС Windows: Windows NT, куда относятся Windows NT, 2000, XP, и Windows 9x (Win 95, 98, ME). При этом отмечается тенденция к отмиранию ветки 9х, хотя такие системы будут встречаться еще достаточно долго. Каждая ветка использует свою архитектуру ядра и подсистемы ввода-вывода. Поэтому естественно, написание драйверов для этих систем должно отличаться.
В Windows 9x долгое время использовались .vxd–драйвера. Эта модель драйверов начинает свою историю еще с Windows 3.1. Для .vxd–драйверов сохранилась совместимость "снизу вверх": т.е. драйвер, написанный под Windows 3.1, будет нормально работать и под Windows 95, а может быть, и 98. Функции драйверов .vxd используются как Win32, так и Win16 приложениями.
В Windows NT 4.0 появилась своя архитектура драйверов. Она ставила перед собой цели повышения устойчивости работы драйвера, переносимости с одной платформы на другую, поддержки многопроцессорности т.п. Вместе с тем архитектура драйверов Windows NT 4.0 была, что называется, "сырой" и недоработанной, хотя и очень перспективной. С выходом систем Win98 и Win2000 появилась новая архитектура драйверов — WDM (Windows Driver Model). Она развилась из архитектуры драйверов Windows NT 4.0 с небольшими изменениями. WDM – драйвера с равным успехом могут быть использованы как в Win 98, так и в Win 2000.
Система Win 98 состоит как бы из двух слоев: User Mode (режим пользователя) и Kernel Mode (режим ядра). В режиме пользователя функционируют пользовательские приложения. Они работают в 3-м кольце защиты; каждая программа работает в своем виртуальном адресном пространстве. Для каждого DOS или Windows–приложения создается своя виртуальная машина (Virtual Machine, VM), задачей которой является виртуализация аппаратуры компьютера для данного приложения. Т.е. каждое приложение считает, что вся оперативная память и все остальные аппаратные ресурсы принадлежат только ему и приложение может обратиться к ним в любой момент. Ядро ОС содержи диспетчер виртуальных машин (Virtual Machine Manager, VMM). Задача VMM — корректно разрешать конфликты, возникающие при доступе к ресурсам системы из разных VM. Ядро, VMМ, виртуальные машины и драйвера виртуальных устройств (Virtual Device Drivers), естественно, работают в режиме ядра (Kernel Mode).
Рис. 1. Подсистема ввода-вывода Win 98.
В Windows 98 обработка запросов на ввод-вывод от приложений DOS и от старых Win16–приложений отличается от обработки запросов новых Win32–приложений. Для DOS–приложений создается своя виртуальная машина (DOS virtual machine), Win 16 и Win32 — приложения используют виртуальную машину Windows (System Virtual Machine). Обычно, когда приложение запрашивает операцию ввода-вывода (например, вызывает функцию API ReadFile — чтение из файла), этот запрос поступает в одну из системных DLL (в нашем случае — kernel32.dll). Оттуда запрос на операцию с внешним устройством передается сразу системным драйверам. Такая организация запроса Приложение→dll→Драйвер получила наибольшее распространение.
Система Windows 2000 имеет другую архитектуру, отличную от Win98. Это обусловлено повышенными требованиями к надежности, защите и переносимости этой системы (теоретически, Win2000 — переносимая система, и существуют реализации Win2000 под системы Alpha, MIPS и др.). В настоящее время именно благодаря этим особенностям Win2000 завоевывает все большую популярность, поэтому стоит рассмотреть особенности ее архитектуры подробнее.
Рис. 2 — главные компоненты Windows2000.
Окружение Win2000 включает компоненты, которые работают в режиме пользователя (User mode) и в режиме ядра (Kernel mode). В режиме пользователя работают подсистема защиты, подсистема Win32-архитектуры (обеспечивает стандартные API — вызовы Windows), подсистема POSIX (обеспечение кроссплатформенности). В режиме ядра работают все основные компоненты системы: диспетчер ввода-вывода (I/O manager), диспетчер конфигурации (Configuration Manager), подсистема PnP, диспетчер управления энергопотреблением (Power Manager), диспетчер памяти (Memory Manager) и прочие жизненно необходимые службы. Драйвера в Win2000 включены в подсистему ввода-вывода. При этом драйвера тесно взаимодействуют практически со всеми компонентами ядра. Драйвера взаимодействуют с аппаратурой при помощи Hardware Abstraction Level, HAL (уровень абстракции аппаратуры). HAL — программный компонент ядра Win2000, который обеспечивает интерфейс ядра (в том числе и некоторых драйверов) с аппаратурой. Т.к. Win2000 – платформенно независимая система (уже сейчас есть версии Win2000 для процессоров Alpha и RISC), то HAL избавляет ядро от непосредственного общения с кэшем, прерываниями, шинами ввода-вывода и большинством прочих устройств, оставляя эту работу драйверам, специально написанным для данной системы. Таким образом, ядро системы представляется набором отдельных изолированных модулей с четко определенными внешними интерфейсами.