Обработка IRP в драйвере Intermediate-Level
Драйверы более высокого уровня имеют другой набор стандартных процедур, чем драйверы устройств самого низкого уровня, с перекрывающимся подмножеством стандартных подпрограмм, общих для обоих типов драйверов.
Набор подпрограмм для драйверов среднего и высшего уровня также зависит от следующих критериев:
Характер базового физического устройства
Указывает, настраивает ли базовый драйвер устройства объекты устройства для прямого или буферизованного ввода-вывода
Проектирование отдельного драйвера более высокого уровня
На следующем рисунке показан путь, который может пройти IRP через стандартные процедуры промежуточного зеркало драйвера, размещенного где-то поверх драйвера устройства самого низкого уровня, описанного в предыдущем разделе.
Драйвер, показанный на следующем рисунке, имеет следующие характеристики:
Драйвер накладывается на несколько физических устройств и, возможно, на несколько драйверов устройств.
Драйвер иногда выделяет дополнительные irP для драйверов более низкого уровня в зависимости от запрошенной операции во входном IRP.
Драйвер имеет по крайней мере один многоуровневый драйвер файловой системы, и этот драйвер файловой системы может быть наложен на другие промежуточные драйверы на более высоком уровне, чем этот.
Как показано на рисунке, диспетчер операций ввода-вывода создает IRP и отправляет его в подпрограмму диспетчеризации драйвера для заданного кода основной функции. При условии, что код функции IRP_MJ_WRITE, подпрограмма диспетчеризации — DDDispatchWrite. Расположение стека ввода-вывода промежуточного драйвера отображается в середине, с затенениями неограниченного числа расположений стека ввода-вывода для драйверов более высокого и нижнего уровней.
Выделение IRP
Зеркало драйвер предназначен для отправки запросов на запись на несколько физических устройств, а также для отправки запросов на чтение попеременно драйверам этих устройств. Для запросов на запись драйвер создает дубликаты irP для каждого устройства, на котором должны быть записаны данные, при условии, что параметры во входном IRP являются допустимыми.
На предыдущем рисунке показан вызов IoAllocateIrp , но драйверы более высокого уровня могут вызывать другие подпрограммы поддержки для выделения IRP для драйверов более низкого уровня. См . статью Создание irP для драйверов Lower-Level.
Когда подпрограмма диспетчеризации вызывает IoAllocateIrp, она указывает количество расположений стека ввода-вывода, необходимых для IRP. Драйвер должен указать расположение стека для каждого более низкого драйвера в цепочке, получая соответствующее значение из объектов устройства каждого драйвера чуть ниже драйвера зеркало. При необходимости драйвер может добавить его к этому значению при вызове IoAllocateIrp , чтобы получить собственное расположение стека для каждого выделяемого IRP, как это делает драйвер на предыдущем рисунке.
Подпрограмма диспетчеризации этого промежуточного драйвера вызывает IoGetCurrentIrpStackLocation (не отображается) с исходным IRP для проверка параметров.
Он вызывает IoSetNextIrpStackLocation , так как он выделил собственное расположение стека в каждом только что созданном IRP и IoGetCurrentIrpStackLocation для создания контекста, который будет использоваться позже в подпрограмме IoCompletion .
Затем он вызывает IoGetNextIrpStackLocation с каждым созданным IRP, чтобы настроить следующие расположения стека ввода-вывода драйверов нижнего уровня в выделенных irP. Подпрограмма диспетчеризации драйвера зеркало копирует коды функций IRP и параметры (указатель на буфер передачи, длина в байтах, которые необходимо передать для IRP_MJ_WRITE) в расположения стека ввода-вывода для следующих ниже драйверов. Эти драйверы, в свою очередь, настроят расположения стека ввода-вывода для драйверов, расположенных непосредственно под ними, если таковые есть.
Вызов IoSetCompletionRoutine и IoCallDriver
Подпрограмма диспетчеризации на предыдущем рисунке вызывает IoSetCompletionRoutine для каждого выделенного IRP. Так как драйвер на предыдущем рисунке должен удалить выделенные ему irP, этот драйвер устанавливает свою подпрограмму IoCompletion , которая будет вызываться, когда более низкие драйверы завершают свои irP, независимо от того, успешно ли выполнена операция ввода-вывода, завершилась ли операция ввода-вывода или была отменена.
Так как драйвер на предыдущем рисунке зеркально отражается параллельно, он передает оба выделенных irP драйверам следующего нижнего уровня, вызывая IoCallDriver дважды, один раз для каждого целевого объекта устройства, представляющего зеркальную секцию.
Обработка IRP в процедуре IoCompletion драйвера
Когда любой из наборов драйверов более низкого уровня завершает запрошенную операцию, диспетчер ввода-вывода вызывает подпрограмму IoCompletion промежуточного зеркало драйвера. Драйвер зеркало поддерживает счетчик в своем собственном расположении стека ввода-вывода для исходного IRP, чтобы отслеживать, когда более низкие драйверы завершили все повторяющиеся irP.
Если предположить, что блок состояния ввода-вывода указывает на то, что один набор более низких драйверов завершил дубликат IRP, показанный на предыдущем рисунке, подпрограмма IoCompletion драйвера зеркало уменьшает свое число, но не может завершить исходный IRP, пока не уменьшит счетчик до нуля. Если уменьшенное число еще не равно нулю, подпрограмма IoCompletion вызывает IoFreeIrp с первым возвращенным IRP (DupIRP1 на предыдущем рисунке), который драйвер выделил и возвращает STATUS_MORE_PROCESSING_REQUIRED.
Когда подпрограмма IoCompletion драйвера зеркало вызывается снова с помощью DupIRP2, показанного на предыдущем рисунке, процедура IoCompletion уменьшает количество в исходном IRP и определяет, что оба набора драйверов более низкого уровня выполнили запрошенные операции.
Предполагая, что блок состояния ввода-вывода в DupIRP2 также задан с STATUS_SUCCESS, подпрограмма IoCompletion копирует блок состояния ввода-вывода из DupIRP2 в исходное IRP и освобождает DupIRP2. Он вызывает IoCompleteRequest с исходным IRP и возвращает STATUS_MORE_PROCESSING_REQUIRED. Если вернуть это состояние, диспетчер операций ввода-вывода не будет пытаться выполнить дальнейшую обработку в DupIRP2; Поскольку IRP не связан с потоком, обработка его завершения должна заканчиваться драйвером, который его создал.
Если любой из наборов драйверов более низкого уровня не завершает выполнение irP драйвера зеркало, процедура IoCompletion драйвера зеркало должна регистрировать ошибку и пытаться выполнить соответствующее восстановление зеркальных данных. Дополнительные сведения см. в разделе Ошибки ведения журнала.