Управление очередями устройств
Диспетчер ввода-вывода обычно (за исключением FSD) создает связанный объект очереди устройств, когда драйвер вызывает IoCreateDevice. Он также предоставляет IoStartPacket и IoStartNextPacket, которые драйверы могут вызывать, чтобы диспетчер ввода-вывода вставлял irp в связанную очередь устройств или вызывал их подпрограммы StartIo .
Следовательно, драйверу редко требуется (или особенно полезно) настраивать собственные объекты очереди устройств для irP. Вероятными кандидатами являются драйверы, такие как драйвер порта SCSI, которые должны координировать входящие irP от некоторого количества тесно связанных драйверов класса для разнородных устройств, обслуживаемых с помощью одного контроллера или адаптера шины.
Другими словами, драйвер для контроллера дискового массива с большей вероятностью использует созданный драйвером объект контроллера, чем для настройки дополнительных объектов очередей устройств, в то время как драйвер для адаптера шины надстройки и набора драйверов класса с немного большей вероятностью будет использовать дополнительные очереди устройств.
Использование дополнительных очередей устройств с помощью процедуры StartIo
Вызывая IoStartPacket и IoStartNextPacket, подпрограммы Dispatch и DpcForIsr (или CustomDpc) драйвера синхронизируют вызовы к своей подпрограмме StartIo с помощью очереди устройств, созданной диспетчером ввода-вывода при создании драйвера объекта устройства. Для драйвера порта с подпрограммой StartIoIoStartPacket и IoStartNextPacket вставьте и удалите irP в очереди устройств для контроллера или адаптера общего устройства драйвера порта. Если драйвер порта также настраивает дополнительные очереди устройств для хранения запросов, поступающих от тесно связанных драйверов более высокого уровня, он должен "отсортировать" входящие IRP по дополнительным очередям устройств, как правило, в своей процедуре StartIo .
Драйвер порта должен определить, к какой дополнительной очереди устройств принадлежит каждое IRP, прежде чем пытаться вставить его в соответствующую очередь. Указатель на объект целевого устройства передается с помощью IRP в подпрограмму диспетчеризации драйвера. Драйвер должен сохранить указатель для использования при "сортировке" входящих irP. Обратите внимание, что указатель объекта устройства, передаваемый в подпрограмму StartIo , является собственным объектом устройства драйвера, который представляет контроллер или адаптер устройства, поэтому его нельзя использовать для этой цели.
После постановки в очередь всех irP драйвер программирует свой общий контроллер или адаптер для выполнения запроса. Таким образом, драйвер порта может обрабатывать входящие запросы для всех устройств в порядке первой очереди, пока вызов KeInsertDeviceQueue не помещает IRP в очередь устройств конкретного класса драйвера.
Используя собственную очередь устройств для всех irP, обрабатываемых с помощью процедуры StartIo , базовый драйвер порта сериализует операции через контроллер или адаптер общего устройства (или адаптер шины) на все подключенные устройства. Иногда, удерживая irP для каждого поддерживаемого устройства в отдельной очереди устройств, этот драйвер порта блокирует обработку irP для уже занятого устройства, увеличивая пропускную способность ввода-вывода для каждого другого устройства, которое выполняет операции ввода-вывода через общее оборудование.
В ответ на вызов IoStartPacket из подпрограммы Dispatch драйвера порта диспетчер ввода-вывода либо немедленно вызывает подпрограмму StartIo этого драйвера, либо помещает IRP в очередь устройства, связанную с объектом устройства для общего контроллера или адаптера драйвера порта.
Драйвер порта должен поддерживать собственные сведения о состоянии каждого из разнородных устройств, обслуживаемых через контроллер или адаптер общего устройства.
При проектировании драйверов классов и портов с дополнительными очередями устройств учитывайте следующее:
Драйвер не может легко получить указатель на объект устройства, созданный любым драйвером, размещенным над собой, за исключением объекта устройства в верхней части стека устройств.
По умолчанию диспетчер операций ввода-вывода не предоставляет процедуры поддержки для получения такого указателя. Кроме того, порядок загрузки драйверов делает невозможным получение указателей для объектов устройств драйверов более низкого уровня, которые еще не были созданы, когда какой-либо драйвер более низкого уровня добавляет свое устройство.
Хотя IoGetAttachedDeviceReference возвращает указатель на объект устройства самого высокого уровня в стеке драйвера, драйвер должен использовать этот указатель только для назначения целевого объекта для запросов ввода-вывода в стек. Драйвер не должен пытаться считывать или записывать объект устройства.
Драйвер не может использовать указатель на объект устройства, созданный любым драйвером, размещенным над собой, за исключением отправки запросов в верхнюю часть собственного стека устройств.
Невозможно синхронизировать доступ к одному объекту устройства (и его расширению устройства) между двумя драйверами в многопроцессорном режиме. Ни один из драйверов не может сделать никаких предположений о том, какие операции ввода-вывода выполняет другой драйвер в настоящее время.
Даже для тесно связанных драйверов классов и портов каждый драйвер класса должен использовать указатель на объекты устройств драйвера порта только для передачи irP с помощью IoCallDriver. Базовый драйвер порта должен поддерживать собственное состояние (вероятно, в расширении устройства драйвера порта) о запросах, которые он обрабатывает для любых устройств тесно связанных драйверов класса.
Управление дополнительными очередями устройств в подпрограммах драйвера
Любой драйвер порта, который помещает IRP в дополнительные очереди устройств для тесно связанного набора драйверов класса, также должен эффективно обрабатывать следующую ситуацию:
Подпрограммы диспетчеризации вставляют irp для определенного устройства в созданную драйвером очередь устройств для этого устройства.
Поставщики irP для других устройств по-прежнему поступают в очередь в подпрограмму StartIo драйвера с помощью IoStartPacket и обрабатываются через общий контроллер устройства.
Контроллер устройства не бездействует, но каждый IRP, хранящиеся в созданной драйвером очереди устройств, также должны быть помещены в очередь в подпрограмму StartIo драйвера как можно скорее.
Следовательно, подпрограмма DpcForIsr драйвера порта должна пытаться передать IRP из внутренней очереди устройств драйвера для конкретного устройства в очередь устройств для общего адаптера или контроллера каждый раз, когда драйвер порта завершает IRP, как показано ниже.
Подпрограмма DpcForIsr вызывает IoStartNextPacket , чтобы подпрограмма StartIo начала обработки следующего IRP, помещенного в очередь на контроллер общего устройства.
Подпрограмма DpcForIsr вызывает KeRemoveDeviceQueue для вывода следующего IRP (если таковой имеется), который он удерживает во внутренней очереди устройств для устройства, от имени которого он должен завершить IRP.
Если KeRemoveDeviceQueue возвращает указатель, отличный от NULL, подпрограмма DpcForIsr вызывает IoStartPacket с только что выведенным из очереди IRP, чтобы поместить его в очередь к общему контроллеру или адаптеру устройства. В противном случае вызов KeRemoveDeviceQueue просто сбрасывает состояние объекта очереди устройства на Not-Busy, а подпрограмма DpcForIsr пропускает вызов IoStartPacket.
Затем подпрограмма DpcForIsr вызывает IoCompleteRequest с входным IRP, для которого драйвер порта только что завершил обработку ввода-вывода, либо установив блок состояния ввода-вывода с ошибкой, либо выполнив запрос ввода-вывода.
Обратите внимание, что предыдущая последовательность подразумевает, что подпрограмма DpcForIsr также должна определить устройство, для которого выполняется текущее (входное) IRP, чтобы эффективно управлять внутренней очередью IRP.
Если драйвер порта пытается подождать, пока его общий контроллер или адаптер не будет простаивать, прежде чем выводить irps из очереди дополнительных устройств, драйвер может голодать устройство, для которого был высокий спрос на операции ввода-вывода, в то время как он быстро обслуживал все остальные устройства, для которых текущий спрос на ввод-вывод был гораздо легче.