Программирование драйверов Windows

         

Адресация и доступ к данным в IRP пакетах чтения/записи


Сразу же после создания объекта устройства (будет ли это сделано в процедуре DriverEntry, как это было указано выше для драйвера "в-стиле-NT", или в процедуре AddDevice для WDM драйверов) рекомендуется явно описать способ, каким новый объект устройства готов воспринимать поля пакета IRP, описывающие адреса областей памяти, через которые будет происходить обмен между драйвером и его клиентами. Например:

PDEVICE_OBJECT pNewDeviceObject; IoCreateDevice(. . . , &pNewDeviceObject); pNewDeviceObject-&#62Flags |= DO_BUFFERED_IO;

либо:

pNewDeviceObject-&#62Flags |= DO_DIRECT_IO;

По умолчанию подразумевается 'pNewDeviceObject-&#62Flags |= 0' &#8212 метод NEITHER (ни один из первых двух).

В том случае, если новый объект устройства ориентирован на работу с нижним драйвером в стеке драйверов, следует скопировать эти флаги из объекта устройства, к которому подключен данный (одним из вызовов IoAttachXxx, см. главу 9), например:

pNewDeviceObject-&#62Flags |= (pUnderlyingDevObject-&#62Flags) & (DO_BUFFERED_IO | DO_DIRECT_IO);

По традиции, главными считаются запросы в форме пакетов IRP с основным кодом IRP_MJ_READ (в результате вызова ReadFile) либо IRP_MJ_WRITE (в результате вызова WriteFile).

Диспетчер ввода/вывода, если он замечает в описании устройства установленный флаг DO_DIRECT_IO, непременно проверяет возможность доступа к буферу, который клиент драйвера указывает в своем запросе как буфер с данными (WRITE) или для данных (READ), подготавливает MDL список для него и фиксирует страницы в оперативной памяти. Адрес подготовленного таким образом MDL списка вносится в поле IRP пакета под названием MdlAddress. Если попробовать получить виртуальный адрес от данного MDL списка вызовом MmGetMdlVirtualAddress, то получится именно виртуальный адрес (пользовательского адресного пространства), который предоставило пользовательское приложение в качестве адреса буфера с выводимыми данными. Адрес в терминах системного адресного пространства для той же области данных можно получить, если вызвать MmGetSystemAddressForMdl.
Когда обработка запроса ввода/ вывода полностью завершена, клиентский буфер де-блокируется и удаляется из схемы распределения системной области памяти.

В том случае, если объект устройства, которому адресован IRР пакет, описан флагом DO_BUFFERED_IO, то драйвер должен взять адрес буферной области из поля IRP пакета AssociatedIrp.SystemBuffer. Данный адрес будет адресом в системном адресном пространстве. Диспетчер ввода/вывода выделяет этот буфер в нестраничной памяти после проверки на доступность предоставленного клиентом буфера. При запросе ввода данных (READ-запрос) Диспетчер ввода/вывода по окончании операции переносит данные из системного буфера в клиентский, а адрес клиентского буфера запоминается в поле IRP пакета UserBuffer. При запросе вывода данных (WRITE-запрос) в системный буфер переносятся данные из клиентского буфера (поле UserBuffer равно NULL). B обоих описанных выше случаях, предоставленные в IRP пакете адреса AssociatedIrp.SystemBuffer можно использовать в произвольном контексте в потоках режима ядра.

В том случае, если устройство не объявило признаков DO_DIRECT_IO или DO_BUFFERED_IO, то Диспетчер ввода/вывода не делает никаких особых действий, а просто помещает адрес буферной области, переданной ему инициатором запроса в поле IRP пакета UserBuffer. Оба поля Associate.SystemBuffer и MdlAddress устанавливаются равными NULL.

В последнем случае помещенный в пoлe UserBuffer виртуальный адрес является "нехорошим" адресом. В большинстве случаев инициатором обращения к драйверу является программный поток пользовательского режима. Соответственно, предоставленный им виртуальный адрес требует для своей корректной интерпретации контекст соответствующего пользовательского потока. Что, разумеется, исключает возможность использования такого адреса в произвольных контекстах режима ядра без предварительной подготовки (подготовки MDL списка и т.п.). Поэтому метод NEITHER можно рекомендовать только для драйверов самых верхних драйверных слоев.

Длина буфера во всех случаях передается в полях Parameters.Write.Length (при WRITE-запросах) или Parameters.Read.Length (при READ-запросах).


Содержание раздела