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

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

Сериализация – это процесс выполнения различных операций в нужной последовательности.

Функция драйвера для обработки запроса ввода/вывода может быть вызвана из различных потоков различных процессов. Неэксклюзивное устройство может быть одновременно открыто несколькими прикладными программами, каждая из которых может послать один и тот же запрос в одно и то же время. Нам необходимо, чтобы обработка каждого такого запроса началась как можно быстрее, но не раньше, чем завершится обработка предыдущего запроса.

Если устройство эксклюзивное и вся обработка завершается в диспетчерской функции, Сериализация обеспечивается операционной системой. Диспетчерские функции работают в контексте памяти потока-инициатора запроса ввода/вывода, следовательно, пока не завершится диспетчерская функция, код прикладной программы не получит управления и не инициирует очередной запрос ввода/вывода.

Однако даже эксклюзивное устройство может не суметь обработать запрос ввода/ вывода в диспетчерской функции сразу при его поступлении. Если драйвер будет ждать в диспетчерской функции возможности обработать и завершить запрос, прикладная программа – инициатор запроса будет "висеть" – ее код не выполняется. Более того, если такой драйвер является уровневым и не является драйвером верхнего уровня, его диспетчерские функции будут вызываться в случайном контексте потока. "Завесив" свою диспетчерскую функцию, драйвер "завесит" произвольный поток произвольной прикладной программы! Чтобы такого не происходило, диспетчерская функция должна завершиться как можно скорее. При этом диспетчеру ввода/вывода необходимо указать, что соответствующий запрос ввода/вывода не был завершен, однако это не ошибка, и этот запрос ввода/вывода будет завершен когда-то позже. При этом возникают вопросы:

  • как указать, что запрос ввода/вывода не завершен;
  • где хранить незавершенные пакеты ввода/вывода;
  • как быть, если запрос ввода/вывода не завершен, а инициировавшее запрос приложение закрылось.

Завершение запроса ввода/вывода.
Успешное завершение запроса ввода/вывода в диспетчерской функции показано в следующем листинге:

Irp › IoStatus.Status = STATUS_SUCCESS;
Irp › IoStatus.Information = bytesTransfered;
loCompleteRequest (Irp, IO_NO_INCREMENT);
return STATUS SUCCESS;

При неудачном завершении обработки запроса ввода/вывода в поле Irp › IoStatus.Status устанавливается код ошибки (значение, не равное STATUS_SUCCESS), оно же возвращается оператором return.

Задержка обработки запросов IRP и постановка запросов IRP в очередь

Когда немедленное завершение запроса ввода/вывода в диспетчерской функции невозможно, драйвер должен указать Диспетчеру ввода/вывода, что обработка запроса продолжается. Для этого возвращаемым значением диспетчерской функции должно быть значение STATUS_PENDING. Перед этим в самом пакете IRP в текущем стеке размещения ввода/вывода должен быть установлен флаг SL_PENDING_RETURNED, помечающий запрос как неоконченный.

Для этого служит функция IoMarkIrpPending().

VOID IoMarkIrpPending(IN PIRP Irp);

Установка этого флага указывает, что IRP будет освобожден драйвером с помощью вызова loCompleteRequest() когда-то потом.

Для постановки пакета IRP в очередь диспетчерская функция должна:

  1. пометить IRP как неоконченный;
  2. установить функцию отмены IRP;
  3. использовать один из нижеприведенных способов для постановки IRP в очередь;
  4. завершиться со статусом STATUS_PENDING.

NT предусматривает два способа организации очередей пакетов IRP:

  • Использование диспетчера ввода/вывода для постановки запросов в управляемую им очередь (Системная Очередь).
  • Создание и управление очередью самостоятельно (Очереди, управляемые драйвером).
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.