[Назад] [Далее]

7.5. Драйверы устройств

В Windows, так же как и в DOS, существует еще один вид исполнимых файлов — драйверы устройств. Windows 3.x и Windows 95 используют одну модель драйверов, Windows NT — другую, a Windows 98 — уже третью, хотя и во многом близкую к модели Windows NT. В Windows 3.x/Windows 95 используются два типа драйверов устройств — виртуальные драйверы (VxD), выполняющиеся с уровнем привилегий 0 (обычно имеют расширение .386 для Windows 3.x и .VXD для Windows 95), и непривилегированные драйверы, исполняющиеся, как и обычные программы, с уровнем привилегий 3 (обычно имеют расширение .DRV). Windows NT использует несовместимую модель драйверов, так называемую kernel-mode (режим ядра). На основе модели kernel-mode с добавлением поддержки технологии PNP и понятия потоков данных в 1996 году была основана модель WDM (win32 driver model), которая теперь используется в Windows 98 и NT и, по-видимому, будет играть главную роль в дальнейшем.

Как и следовало ожидать, основным средством создания драйверов является ассемблер, и хотя применение С здесь возможно (в отличие от случая драйверов DOS), оно оказывается гораздо сложнее, чем применение ассемблера, — функции, к которым обращается драйвер, могут передавать параметры в регистрах, сегменты, из которых состоит драйвер, должны называться определенным образом, и т.д. Практически при создании драйверов часто пользуются одновременно С и ассемблером или С и специальным пакетом, который включает в себя все необходимые для инициализации действия.

Чтобы самостоятельно создавать драйверы для любой версии Windows, необходим комплект программ, документации, включаемых файлов и библиотек, распространяемый Microsoft, который называется DDK (Drivers Development Kit). (DDK для Windows NT и Windows 98 распространяются бесплатно.)

Мы не будем рассматривать программирование драйверов для Windows в деталях, так как этой теме посвящено достаточно много литературы, не говоря уже о документации, прилагающейся к DDK. Чтобы создать драйвер, в любом случае лучше всего начать с одного из прилагающихся примеров и изменять/добавлять процедуры инициализации, обработчики сообщений, прерываний и исключений, обработчики для API, предоставляемого драйвером, и т.д. Рассмотрим только, как выглядит исходный текст драйвера, потому что он несколько отличается от привычных нам ассемблерных программ.

Любой драйвер начинается с директивы include vmm.inc, которая включает файл, содержащий определения используемых сегментов и макроопределений. Макроопределения вида VXD_LOCKED_CODE_SEG/VXD_LOCKED_CODE_ENDS соответствуют директивам начала и конца соответствующих сегментов (в данном случае сегмента _LTEXT). Другие два важных макроопределения Declare_Virtual_Device и VMMCall/WDMCall. Первое — это просто определение, получающее в качестве параметров идентификатор драйвера, название, версию, порядок загрузки и адреса основных процедур драйвера, из которых строится заголовок драйвера. Второе — это замена команды call, получающая в качестве параметра имя функции VMM или WDM, к которой надо обратиться. Например, элемент кода драйвера BIOSXLAT, перехватывающий прерывание 10h, выглядит следующим образом:

VxD_ICODE_SEG                          ; начало сегмента _ITEXT (сегмент кода
                                       ; инициализации,  исполняющийся
                                       ; в защищенном режиме, который удаляется
                                       ; из памяти после сообщения Init_Complete)
BeginProc BIOSXlat_Sys_Critical_Init   ; процедура, которая вызывается
                                       ; для обработчика сообщения
                                       ; Sys_Critical_Init - первого сообщения,
                                       ; которое получает драйвер.
                                       ; Обычно обработчики сообщений должны
                                       ; сохранять регистры ЕВХ, EDI, ESI и ЕВР,
                                       ; хотя в этом случае этого можно не делать
        mov        esi,OFFSET32 BIOSXlat_Int10 ; адрес обработчика INT 10h
                                             ; в регистр ESI. Важно использовать
                                             ; макроопределение OFFSET32 всюду
                                             ; вместо offset
        mov        edx,10h                   ; любое число, которое будет
                                             ; помещаться в EDX при вызове
                                             ; регистрируемого обработчика
        VMMCall    Allocate_PM_Call_Back     ; зарегистрировать
                                             ; точку входа, обращаясь
                                             ; к которой, программы из защищенного
                                             ; режима в VM будут передавать
                                             ; управление процедуре в драйвере
                                             ;(но не win32-nporpaммa) -
                                             ; они должны использовать
                                             ; DeviceIOControl для работы
                                             ; с драйверами,
        jc         BXLSCI_NoPM               ; если CF = 1 - произошла ошибка
        xchg       edx,eax                   ; точку входа - в EDX,
                                             ; число 10h - в ЕАХ
; (теперь это - номер перехватываемого прерывания для Set_PM_Int_Vector)
        mov        ecx,edx
        shr        ecx,10h                   ; селектор точки входа
        movzx      edx,dx                    ; смещение точки входа
        VMMCall    Set_PM_Int_Vector         ; установить обработчик
                                             ; прерывания INT 10h.
; Если эта функция вызвана до Sys_VM_Init, установленный обработчик
; становится звеном цепочки обработчиков для всех виртуальных машин.
; После того как прерывание проходит по цепочке обработчиков
; в защищенном режиме, оно отображается в V86, точно так же, как в DPMI

; [код перехвата других прерываний]

EndProc BIOSXlat_Sys_Critical_Init     ; конец процедуры
VxD_ICODE_ENDS                         ; конец сегмента инициализации

Соответственно, чтобы эта процедура была вызвана для сообщения Sys_Critical_Init, в сегменте фиксированного кода _LTEXT должна быть следующая запись:

VxD_LQCKED_CODE_SEG                    ; начало сегмента _LTEXT
BeginProc       BIOSXlat_Control       ; начало процедуры
                Control_Dispatch Sys_Critical_Init,\BIOSXlat_Sys_Critical_Init
; при помощи еще одного макроопределения из vmm.inc
; зарегистрировать процедуру BIOSXlat_Sys_Critical_Init
; как обработчик сообщения Sys_Critical_Init
                clc                    ; процедура-обработчик управляющих
                                       ; сообщений должна возвращать CF = 0
                ret
EndProc         BIOSXlat_Control       ; конец процедуры
VxD_LOCKED_CODE_ENDS                   ; конец сегмента _LTEXT

И наконец, процедура BIOSXlat_Control регистрируется как процедура, получающая управляющие сообщения, в заголовке драйвера:

; первая строка после .386р и include vmm.inc:
Declare_Virtual_Device  BlOSXlat, 1, 0, BIOSXlat_Control,\ BIOSXiat_Device_ID,
                        BIOSXlat_Init_Order

Это не слишком сложно и, пользуясь примерами и документацией из DDK и отладчиком SoftICE, можно справиться практически с любой задачей, для которой имеет смысл создавать драйвер.


п»ї
"target=_blank><\/a>") //-->