Случай 3: Работа через очереди IRP пакетов
Разумеется, простейший драйвер может инициализировать свое устройство в процедуре DriverEntry, в обработчике запросов IRP_MJ_READ сразу же считывать данные из устройства (например, LPT порта). Тем не менее, полномасштабный ритуал работы с подсистемой ввода/вывода Windows (то есть Диспетчером ввода/вывода) диктует иную последовательность действия. Рабочая процедура должна поместить пакет IRP в очередь для последующей обработки процедурой StartIO и сразу же возвратить Диспетчеру ввода/вывода сообщение о том, что обработка IRP не завершена, а именно:
Фрагмент кода, приведенный ниже, демонстрирует, как рабочая процедура размещает IRP запрос в очереди на обработку.
NTSTATUS ReadRequestHandler( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { : // IRP "в работе", но работа с ним будет происходить // через очередь пакетов (в данном случае - системную): IoMarkIrpPending( pIrp );
// Четвертый параметр позволяет указывать процедуру удаления // CancelRoutine, что подробно обсуждается в конце главы 9. IoStartPacket( pDeviceObject, pIrp, 0, NULL );
return STATUS_PENDING; }
Некоторые источники указывают, что Диспетчер ввода/вывода автоматически завершает запросы, не помеченные кодом STATUS_PENDING, сразу же после получения управления из рабочей процедуры. Возможно, данный автоматически запускающийся механизм когда-то и работал именно так. Однако в доступных на сегодня версиях Windows это не подтверждается, то есть для нормального завершения обработки IRP пакета необходимо, чтобы текущий драйвер производил вызов IoCompleteRequest
в конце обработки IRP пакета в своей рабочей процедуре (если только он не помечен как отложенный в системную очередь, STATUS_PENDING). К тому же, при этом условии будут запущены все процедуры завершения в вышестоящих драйверах, если таковые, конечно, имеются. Подробнее эти вопросы рассмотрены в главе 9.