Обработка IRP в драйвере Lowest-Level
У физических драйверов самого низкого уровня есть определенные стандартные процедуры, которые не требуются драйверам более высокого уровня. Набор стандартных процедур для драйверов самого низкого уровня также зависит от следующих критериев:
Характер устройства, который управляет каждым драйвером
Указывает, настраивает ли драйвер объекты устройства для прямого или буферизованного ввода-вывода
Проектирование отдельного драйвера
Чтобы проиллюстрировать роли стандартных подпрограмм драйвера, на следующем рисунке показан путь, который может использовать образец IRP, так как он обрабатывается драйвером устройства с запоминающей памятью самого низкого уровня. Драйвер на рисунке имеет следующие характеристики:
Устройство создает прерывания в конце каждой операции ввода-вывода, поэтому этот драйвер имеет подпрограммы ISR и DpcForIsr .
Драйвер имеет подпрограмму StartIo , а не настраивать внутренние очереди для IRP и управлять собственной очередью.
Драйвер использует системное DMA, поэтому он устанавливает флаги своих объектов устройства для прямого ввода-вывода и имеет подпрограмму AdapterControl .
Как показано на этом рисунке, диспетчер операций ввода-вывода создает IRP и отправляет его в подпрограмму диспетчеризации драйвера для заданного кода основной функции. Если код функции IRP_MJ_READ илиIRP_MJ_WRITE, подпрограмма диспетчеризации — DDDispatchReadWrite.
Вызов IoGetCurrentIrpStackLocation
Любая подпрограмма драйвера, требующая параметров IRP, должна вызывать IoGetCurrentIrpStackLocation , чтобы получить расположение стека ввода-вывода драйвера. К таким подпрограммам относятся подпрограммы диспетчеризации, которые обрабатывают несколько основных кодов функций ввода-вывода (IRP_MJ_*XXX), обрабатывают функцию, поддерживающую вспомогательные функции (IRP_MN_XXX), или обрабатывают запросы управления вводом-выводом устройства (*IRP_MJ_DEVICE_CONTROL и /или IRP_MJ_INTERNAL_DEVICE_CONTROL), а также все остальные подпрограммы драйвера, обрабатывающие IRP.
Расположение стека ввода-вывода этого драйвера является самым низким, с затенениями неограниченного числа расположений стека ввода-вывода драйверов более высокого уровня. Для простоты вызовы IoGetCurrentIrpStackLocation из подпрограмм DispatchReadWrite, StartIo, AdapterControl и DpcForIsr не показаны на предыдущем рисунке.
Вызов IoMarkIrpPending и IoStartPacket
Пример драйвера не завершает IRP в своей подпрограмме диспетчеризации, а обрабатывает IRP в своей подпрограмме StartIo . Прежде чем это сделать, подпрограмма диспетчеризации вызывает IoMarkIrpPending , чтобы указать, что IRP еще не завершен. Затем он вызывает IoStartPacket для постановки IRP в очередь для дальнейшей обработки с помощью процедуры StartIo драйвера. Подпрограмма диспетчеризации также возвращает значение NTSTATUS STATUS_PENDING.
На следующем рисунке показан вызов IoStartPacket.
Если драйвер занят обработкой другого IRP на устройстве, IoStartPacket вставляет IRP в очередь устройства, связанную с объектом устройства. При необходимости драйвер может предоставить значение Ключа в качестве параметра в IoStartPacket , чтобы наложить определяемый драйвером порядок irP в очереди устройств.
Если драйвер не занят, а очередь устройств пуста, диспетчер ввода-вывода немедленно вызывает свою подпрограмму StartIo , передав входной IRP.
Для запоминающих устройств драйверу самого низкого уровня не нужно предоставлять подпрограмму Отмены при вызове IoStartPacket по двум причинам:
Файловая система, наложенная на такой драйвер, обычно обрабатывает отмену запросов ввода-вывода файлов.
Драйверы запоминающих устройств быстро обрабатывают irP.
Как правило, драйвер самого высокого уровня в цепочке многоуровневых драйверов обрабатывает отмену IRP.
Вызовы AllocateAdapterChannel и MapTransfer
Предполагая, что подпрограмма StartIo , показанная на рисунке, иллюстрирующая путь IRP через подпрограммы драйвера самого низкого уровня, определяет, что запрос на передачу может быть выполнен одной операцией DMA, подпрограмма StartIo вызывает AllocateAdapterChannel с точкой входа подпрограммы AdapterControl драйвера и IRP.
Когда системный контроллер DMA доступен, диспетчер ввода-вывода вызывает подпрограмму AdapterControl драйвера, чтобы настроить операцию передачи. Подпрограмма AdapterControl вызывает MapTransfer для настройки системного контроллера DMA. Затем драйвер программирует свое устройство для операции DMA и возвращает . (Дополнительные сведения об использовании объектов DMA и адаптеров см. в разделе Методы ввода-вывода.)
Вызов IoRequestDpc из ISR драйвера
Когда устройство прерывается, чтобы указать, что его операция передачи завершена, ISR драйвера останавливает устройство от создания прерываний и вызывает IoRequestDpc, как показано на рисунке, иллюстрируя путь IRP через подпрограммы драйвера самого низкого уровня.
Этот вызов помещает в очередь подпрограмму DpcForIsr драйвера, чтобы завершить как можно больше операций передачи с более низким приоритетом оборудования (IRQL).
Вызов IoStartNextPacket и IoCompleteRequest
Когда подпрограмма DpcForIsr завершает обработку передачи, она немедленно вызывает IoStartNextPacket , чтобы подпрограмма StartIo драйвера была вызвана со следующим IRP в очереди устройств, если они находятся в очереди. Подпрограмма DpcForIsr также задает только что завершенный блок состояния ввода-вывода IRP, а затем вызывает IoCompleteRequest для IRP.
На следующем рисунке показаны вызовы этого драйвера к IoStartNextPacket и IoCompleteRequest.
Драйверы должны вызывать IoStartNextPacket или IoStartNextPacketByKey , чтобы как можно скорее начать следующую запрошенную операцию ввода-вывода, предпочтительно до вызова IoCompleteRequest.
Если какие-либо irP помещаются в очередь для устройства, IoStartNextPacket вызывает KeRemoveDeviceQueue , чтобы удалить следующий IRP из очереди. Затем диспетчер операций ввода-вывода вызывает подпрограмму StartIo драйвера, передавая выведенное из очереди IRP. Если в настоящее время в очереди устройств нет irP, IoStartNextPacket просто возвращается вызывающей.
Установка блока состояния ввода-вывода в IRP
Каждый драйвер самого низкого уровня должен задать блок состояния ввода-вывода IRP перед вызовом IoCompleteRequest. (На предыдущем рисунке вторая затеняемая область обозначает блок состояния.) Блок состояния ввода-вывода предоставляет сведения драйверам более высокого уровня и, в конечном счете, исходному инициатору запроса операции ввода-вывода. Любой драйвер более высокого уровня, размещенный над драйвером на предыдущем рисунке, мог настроить подпрограмму IoCompletion , которая считывает блок состояния ввода-вывода, заданный этим драйвером. Драйверы более высокого уровня обычно не изменяют блок состояния ввода-вывода в IRP, который был выполнен драйвером устройства, если только драйвер более высокого уровня не повторяет IRP, в этом случае он повторно инициализирует блок состояния ввода-вывода.
Каждый драйвер более высокого уровня, который завершает IRP, не отправляя его следующему более низкому драйверу, также должен задать блок состояния ввода-вывода в этом IRP перед вызовом IoCompleteRequest. Для хорошей общей пропускной способности ввода-вывода драйвер более высокого уровня должен проверка параметры в собственном расположении стека ввода-вывода каждого IRP и, если параметры недопустимы, установить блок состояния ввода-вывода и завершить сам запрос. По возможности драйвер не должен передавать недопустимый запрос в более низкие драйверы в цепочке.
Если операция передачи на предыдущем рисунке выполнена успешно, подпрограмма DpcForIsr , показанная на рисунке, иллюстрирующая путь IRP через подпрограммы драйвера самого низкого уровня, задает STATUS_SUCCESS в разделе Состояние и количество байтов, переданных в разделе Сведения для блока состояния ввода-вывода IRP.
Многие стандартные подпрограммы драйвера также возвращают значения типа NTSTATUS. Дополнительные сведения о константах NTSTATUS, таких как STATUS_SUCCESS, см. в разделе Ошибки ведения журнала.