Иллюстрированный самоучитель по программированию систем защиты

Сериализация

Системная очередь запросов IRP (System Queuing)

Простейший способ, с помощью которого драйвер может организовать очередь IRP – использовать Системную Очередь. Для этого драйвер предоставляет диспетчерскую точку входа Startle (DriverObject › DriverStartIo). При получении пакета IRP, который необходимо поставить в Системную Очередь, такой пакет необходимо пометить как отложенный с помощью вызова IoMarkIrpPending(), а затем вызвать функцию IoStartPacket().

VOID loStartPacket(IN PDEVICEJDBJECT Device-Object, IN PIRP Irp, IN PULONG Key,
IN PDRIVER_CANCEL CancelFunction);

Где:

  • DeviceObject – Указатель на устройство, которому направлен запрос ввода/ вывода;
  • Irp – Указатель на пакет IRP, описывающий запрос ввода/вывода;
  • Key – Необязательный указатель на значение, определяющее позицию в очереди IRP, в которую будет вставлен пакет IRP. Если указатель NULL, IRP помещается в конец очереди;
  • CancelFunction – Необязательный указатель на функцию – точку входа драйвера, которая будет вызвана при отмене данного запроса ввода/вывода диспетчером ввода/ вывода.

Вызов этой функции ставит пакет IRP для данного устройства в Системную Очередь. При этом, если в момент вызова loStartPacket() функция Startlo не выполняется, происходит выборка из очереди очередного IRP.

При выборке очередного пакета IRP из очереди, если очередь не пуста, для очередного IRP будет вызвана функция Startlo. Функция имеет такой же прототип, как и все диспетчерские функции, но вызывается в случайном контексте потока на уровне IRQL DISPATCH_LEVEL: NTSTATUS Startlo (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp).

Структура функции Startlo практически такая же, как у диспетчерской функции, не использующей организацию очередей, за двумя важными исключениями:

  • Startlo вызывается в случайном контексте потока на уровне IRQL DISPATCH_ LEVEL. Поэтому необходимо: а) соблюдать все ограничения для уровня IRQL DISPATCH_LEVEL; б) не использовать метод передачи буфера Neither, так как такой метод передачи предполагает знание контекста памяти процесса, из которого был инициирован запрос ввода/вывода (см. раздел "Описание Буфера Данных").
  • Какой бы ни был результат обработки пакета IRP, после завершения его обработки Startlo обязана вызвать функцию IoStartNextPacket() для указания диспетчеру ввода/вывода необходимость выбора из системной очереди очередного пакета IRP (то есть вызвать для него Startlo) сразу после завершения Startlo для текущего пакета.

Прототип функции IoStartNextPacket():

VOID IoStartNextPacket(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Cancel able);

Где:

  • DeviceObject – Указатель на устройство, для которого необходимо выбрать следующий пакет из системной очереди IRP;
  • Cancelable – Указывает, может ли выбираемый из очереди пакет IRP быть отменен в процессе обработки. Если при вызове loStartPacketQ была указана ненулевая функция отмены, параметр Cancelable обязан быть равным TRUE.

Вместо IoStartNextPacket() может быть использована функция loStartNext PacketByKey():

VOID loStartNextPacketByKey(IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Cancelable, IN ULONG Key);

В этом случае из очереди будет выбран очередной пакет IRP с заданным значением Key (это значение могло быть установлено при помещении пакета в очередь при вызове loStartPacket()).

При использовании системной очереди диспетчер ввода/вывода отслеживает, когда данный объект-устройство свободен или занят обработкой очередного IRP. Это осуществляется с помощью специального объекта ядра Очередь Устройства (Device Queue Object, тип – структура KDEVICE_QUEUE), помещенного внутрь объекта-устройства в поле DeviceQueue. Поле DeviceQueue.Busy устанавливается диспетчером ввода/вывода равным TRUE непосредственно перед вызовом функции Startlo, и FALSE, если вызвана функция IoStartNextPacket(ByKey)(), а системная очередь не содержит пакетов IRP для данного устройства. При обработке очередного IRP указатель на него находится в объекте-устройстве в поле Currentlrp.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.