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