- •Лекция №10. Сериализация. System Queuing и Driver Queuing
- •[10.1] Завершение запроса в/в.
- •[10.2] Задержка обработки запросов irp и постановка запросов irp в очередь [10.2.1] Задержка обработки запросов irp
- •[10.2.2] Постановка запросов irp в очередь
- •[10.2.2.1] Системная очередь запросов irp (System Queuing)
- •[10.2.2.1.1] Обработка пакетов irp в функции StartIo
- •[10.2.2.2] Очереди, управляемые драйвером
- •[10.2.2.2.1] Функции управления очередью низкого уровня
- •[10.2.2.2.2] Функции управления очередью высокого уровня – “Очередь Устройства” (Device Queue)
- •[10.3] Отмена запросов в/в
- •[10.3.1] Отмена irp и Системная Очередь
- •[10.3.2] Отмена irp и очереди, управляемые драйвером
[10.2.2.1] Системная очередь запросов irp (System Queuing)
Простейший способ, с помощью которого драйвер может организовать очередь IRP – использовать Системную Очередь. Для этого драйвер предоставляет диспетчерскую точку входа StartIo (DriverObject->DriverStartIo). При получении пакета IRP, который необходимо поставить в Системную Очередь, такой пакет необходимо пометить как отложенный с помощью вызова IoMarkIrpPending(), а затем вызвать функцию IoStartPacket().
VOID IoStartPacket(IN PDEVICE_OBJECTDeviceObject,
IN PIRP Irp,
IN PULONG Key,
IN PDRIVER_CANCEL CancelFunction);
DeviceObject: Указатель на устройство, которому направлен запрос в/в
Irp: Указатель на пакет IRP, описывающий запрос в/в
Key: Необязательный указатель на значение, определяющее позицию в очереди IRP, в которую будет вставлен пакет IRP. Если указатель NULL, IRP помещается в конец очереди.
CancelFunction: Необязательный указатель на функцию – точку входа драйвера, которая будет вызвана при отмене данного запроса в/в диспетчером в/в.
Вызов этой функции ставит пакет IRP для данного устройства в Системную Очередь. При этом, если в момент вызова IoStartPacket() функция StartIo не выполняется, происходит выборка из очереди очередного IRP.
При выборке очередного пакета IRP из очереди, если очередь не пуста, для очередного IRP будет вызвана функция StartIo. Функция имеет такой же прототип, как и все диспетчерские функции, но вызывается в случайном контексте потока на уровне IRQL DISPATCH_LEVEL:
NTSTATUS StartIo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp).
Структура функции StartIo практически такая же, как у диспетчерской функции, не использующей организацию очередей, за двумя важными исключениями:
StartIo вызывается в случайном контексте потока на уровне IRQL DISPATCH_LEVEL. Поэтому необходимо: а) соблюдать все ограничения для IRQL DISPATCH_LEVEL; б) не использовать метод передачи буфера Neither. Такой метод передачи предполагает знание контекста памяти процесса, из которого был инициирован запрос в/в (см. лекцию 7 п.[7.3] “Описание Буфера Данных”).
Какой бы ни был результат обработки пакета IRP, после завершения обработки IRP StartIo обязана вызвать функцию IoStartNextPacket()для указания диспетчеру в/в выбрать из системной очереди очередной пакет IRP (т.е. вызвать для него StartIo) сразу после завершения StartIo для текущего пакета.
Прототип функции IoStartNextPacket():
VOID IoStartNextPacket(IN PDEVICE_OBJECTDeviceObject, IN BOOLEANCancelable);
DeviceObject:Указатель на устройство, для которого необходимо выбрать следующий пакет из системной очереди IRP.
Cancelable:Указывает, может ли выбираемый из очереди пакет IRP быть отменен в процессе обработки. Если при вызове IoStartPacket() была указана ненулевая функция отмены, параметр Cancelable обязан быть равным TRUE.
Вместо IoStartNextPacket() может быть использована функция IoStartNextPacketByKey():
VOID IoStartNextPacketByKey(IN PDEVICE_OBJECTDeviceObject,
IN BOOLEAN Cancelable, IN ULONGKey);
В этом случае из очереди будет выбран очередной пакет IRP с заданным значением Key (это значение могло быть установлено при помещении пакета в очередь при вызове IoStartPacket()).
При использовании системной очереди диспетчер в/в отслеживает, когда данный объект-устройство свободен или занят обработкой очередного IRP. Это осуществляется с помощью специального объекта ядра Очередь Устройства (Device Queue Object, тип – структура KDEVICE_QUEUE), помещенного внутрь объекта-устройство в поле DeviceQueue. Поле DeviceQueue.Busy устанавливается диспетчером в/в равным TRUE непосредственно перед вызовом функции StartIo, и FALSE, когда вызвана функция IoStartNextPacket(ByKey)(), а системная очередь не содержит пакетов IRP для данного устройства. При обработке очередного IRP указатель на него находится в объекте-устройстве в поле CurrentIrp.