Работа с MDL списками
Как было сказано выше, когда поступает IOCTL запрос от пользовательского приложения, в котором метод буферизации указан METHOD_IN_DIRECT либо METHOD_OUT_DIRECT, Диспетчер ввода/вывода выполняет построение MDL списка для выходного буфера (5-й параметр в пользовательском вызове DeviceIoControl). Указатель на этот MDL список передается в драйвер внутри структуры IRP пакета. Драйвер может зафиксировать (lock) область этого буфера в оперативной памяти вызовом MmProbeAndLockPages, после чего с этим буфером можно работать в разных контекстах, в том числе на высоких уровнях IRQL (DISPATCH_LEVEL и выше).
Драйвер не обязан ограничиваться использованием только лишь получаемых от Диспетчера ввода/вывода MDL списков. Он может создавать собственные MDL списки, что бывает совершенно необходимо при работе с устройствами, поддерживающими DMA операции. Ниже приводятся сведения о наиболее часто употребляемых функциях для работы с MDL списками.
Таблица 7.17. Прототип вызова MmSizeOfMdl
ULONG MmSizeOfMdl | IRQL — любой |
Параметры | Определяет размер структуры для MDL списка, который будет описывать область данного размера |
IN PVOID Base | Виртуальный адрес области, для которой следует определить размер предполагаемого MDL списка |
IN ULONG Length | Размер описываемой области в байтах |
Возвращаемое значение | Размер, необходимый для хранения MDL списка |
Таблица 7.18. Прототип вызова MmCreateMdl
PVOID MmCreateMdl | IRQL < DISPATCH_LEVEL | |
Параметры | Создает MDL список и инициализирует его (в документации DDK XP объявлена устаревшей функцией и вместо нее рекомендуется использовать вызов IoAllocateMdl) | |
IN PMDL OPTIONAL pMemoryDescriptorList | Указатель на область размером не менее sizeof(MDL) или NULL. Во втором случае вызов самостоятельно выделяет память в нестраничной памяти. | |
IN PVOID Base | Виртуальный адрес области, для которой следует построить MDL | |
IN ULONG Length | Размер буфера в байтах | |
Возвращаемое значение | Указатель на MDL список или NULL (при ошибке) |
Таблица 7.19. Прототип вызова IoAllocateMdl
PVOID IoAllocateMdl | IRQL <= DISPATCH_LEVEL |
Параметры | Создает MDL список и инициализирует его. При этом можно выполнить его привязку к нужному IRP пакету |
IN PVOID VAddress | Виртуальный адрес области, для которой будет построен MDL список |
IN ULONG Length | Размер виртуальной области, для которой следует построить MDL |
IN BOOLEAN SecondaryBuffer | Если pIrp равен NULL, см. ниже, то и параметр SecondaryBuffer должен быть равен NULL. Если параметр pIrp не равен NULL, тогда при: FALSE — указатель на созданную структуру MDL списка будет занесен в поле pIrp->MdlAddress TRUE — поле Next созданной MDL структуры будет указывать на MDL список, который прописан в поле pIrp->MdlAddress |
IN BOOLEAN ChargeQuota | Если TRUE — вычесть из квоты текущего программного потока на создание MDL списков. Обычно применяется FALSE |
IN OUT PIRP pIrp | NULL — не работаем с каким-либо IRP пакетом. Иначе — см. описание SecondaryBuffer выше |
Возвращаемое значение | Указатель на MDL список. Замечание. Инициализацию MDL списка можно считать завершенной только после вызова MmProbeAndLockPages, см. ниже |
Таблица 7.20. Прототип вызова MmInitializeMdl
VOID MmInitializeMdl | IRQL <= DISPATCH_LEVEL |
Параметры | Выполняет инициализацию MDL списка |
IN PMDL OPTIONAL pMdl | Указатель на буфер для хранения MDL списка. |
IN PVOID Base | Виртуальный адрес области, для которой следует построить MDL |
IN ULONG Length | Размер буфера в байтах |
Возвращаемое значение |
void Замечание. Инициализацию MDL списка можно считать завершенной только после вызова MmProbeAndLockPages, см. ниже |
Следует обратить внимание на то, как происходит завершение работы с MDL списками. Действия, выполненные функцией MmProbeAndLockPages, отменяются вызовом MmUnlockPages (см. ниже), а инициализированный MDL список следует "деактивировать" вызовом IoFreeMdl. Затем, если драйвер перед инициализацией самостоятельно выделял память под структуру MDL списка, то ее также следует явно освободить соответствующим системным вызовом (например, ExFreePool).
Таблица 7.21. Прототип вызова IoFreeMdl
VOID IoFreeMdl | IRQL <= DISPATCH_LEVEL |
Параметры | Выполняет очистку MDL списка |
IN PMDL pMdl | Указатель на структуру, описывающую MDL список |
Возвращаемое значение | void |
VOID MmProbeAndLockPages | IRQL < DISPATCH_LEVEL |
Параметры | Выполняет фиксацию страниц, описанных в MDL списке, в физической памяти |
IN OUT PMDL pMdl | Указатель на MDL список |
IN KPROCESSOR_MODE AccessMode | Режим, в котором будет проверяться доступ к анализируемым страницам: KernelMode UserMode |
IN LOCK_OPERATION Operation |
Права доступа после того, как страницы будут зафиксированы. Одно из значений: IoReadAccess IoWriteAccess IoModifyAccess |
Возвращаемое значение | void Замечание. Часть MDL списка, описывающая физические страницы, после данного вызова, скорее всего, будет обновлена. |
VOID MmUnlockPages | IRQL <= DISPATCH_LEVEL |
Параметры | Отменяет фиксацию страниц страничной памяти в оперативной памяти |
IN PMDL pMdl | Указатель на структуру, описывающую MDL список, соответствующий набору зафиксированных в оперативной памяти страниц |
Возвращаемое значение | void |
Таблица 7.24. Прототип вызова MmGetSystemAddressForMdl
PVOID MmGetSystemAddressForMdl | IRQL <= DISPATCH_LEVEL |
Параметры | Показывает системный виртуальный адрес начала буфера, описываемого MDL списком |
IN PMDL pMdl | Указатель на структуру MDL списка |
Возвращаемое значение | Адрес диапазона системных адресов |
Таблица 7.25. Прототип вызова MmGetMdlByteCount
ULONG MmGetMdlByteCount | IRQL <= DISPATCH_LEVEL |
Параметры | Показывает размер буфера, описываемого MDL списком |
IN PMDL pMdl | Указатель на структуру MDL списка |
Возвращаемое значение | Размер области, описываемой MDL списком |
ULONG MmGetMdlByteOffset | IRQL <= DISPATCH_LEVEL |
Параметры | Показывает смещение начала буфера на первой странице буферной области, описываемой данным MDL списком |
IN PMDL pMdl | Указатель на структуру MDL списка |
Возвращаемое значение | Смещение |
PVOID MmGetMdlVirtualAddress | IRQL — любой |
Параметры | Показывает виртуальный адрес начала буфера, описываемого MDL списком |
IN PMDL pMdl | Указатель на структуру MDL списка |
Возвращаемое значение | Исходный виртуальный адрес описываемой данным MDL списком области памяти. Замечание. В том случае, если область была предоставлена пользовательским приложением, здесь можно увидеть виртуальный адрес из диапазона пользовательского адресного пространства (для интерпретации которого требуется контекст этого приложения). |
На самом деле, в составе пакета DDK имеется вызов, который позволит избежать многих бесполезных этапов в подготовке новой MDL структуры, если имеется другая, бывшая в употреблении ранее. Соответствующая функция называется MmPrepareMdlForReuse, см. описание ниже.
Таблица 7.28. Прототип вызова MmPrepareMdlForReuse
VOID MmPrepareMdlForReuse | IRQL <= DISPATCH_LEVEL |
Параметры | Подготавливает MDL список к повторному использованию |
IN PMDL pMdl | Указатель на структуру MDL списка |
Возвращаемое значение | void |