[Previous] [Next]

Рабочие процедуры обслуживания ввода/вывода

В момент, когда Диспетчер ввода/вывода получает от приложения, работающего в пользовательском режиме, запрос на операцию ввода/вывода, происходит преобразование типа запроса (чтения, записи и т.п.) в код функции запроса. Диспетчер ввода/вывода идентифицирует надлежащий драйвер, которому следует адресовать запрос, после чего производит вызов одной из рабочих (dispatch) процедур этого драйвера.

Вызванная драйверная процедура проверяет запрос и либо его обрабатывает, либо, при необходимости, делает запрос к Диспетчеру ввода/вывода, чтобы он отложил запрос к устройству для последующей реальной работы с ним. Во втором случае, вызванная рабочая процедура возвращает управление Диспетчеру ввода/вывода, помечая поступивший запрос как незавершенный (pending) — это случай простой задержки обработки IRP запроса (отличающийся от применения DPC процедур для завершения обработки прерывания).

Обработчики запросов Open и Close

Все драйверы, если только они не тестового назначения, должны иметь в своем составе процедуру CreateDispatch (в примере Example.sys эта процедура называлась Create_File_IRPprocessing), которая производит обработку пользовательского запроса CreateFile. Помимо того, драйверы, которые должны выполнять действия по очистке в ответ на пользовательский запрос CloseHandle, обязаны иметь в своем распоряжении еще и процедуру CloseDispatch (в примере Example.sys — процедура Close_HandleIRPprocessing).

Процедуры передачи данных

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

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

Драйвер Example.sys для обслуживания указанных запросов зарегистрировал функции ReadWrite_IRPhandler и DeviceControlRoutine.

Процедура StartIo

Выполняя регистрацию рабочей процедуры StartIo, драйвер соглашается участвовать в процессе, называющемся System Queuing, то есть создание очередей необработанных запросов при помощи системных средств — в отличие от Driver Queuing— ведение учета необработанных запросов средствами собственно драйвеpa (Для создания и ведения внутренних очередей драйверы используют вызовы KeInitalizeDeviceQueue и прочие Ke...Queue).

В том случае, если какая-либо из рабочих процедур (например, обработки запросов Read-Write) не может завершить обработку запроса сразу, то она помечает текущий IRP пакет как 'pending' при помощи вызова, а на самом деле — макроопределения (см. файлы wdm.h или ntddk.h) IoMarkIrpPending, таблица 9.12. После этого она делает вызов IoStartPacket и возвращает управление Диспетчеру ввода/вывода с кодом STATUS_PENDING.

В результате вызова IoStartPacket Диспетчер ввода/вывода вызывает драйверную процедуру StartIo или, если устройство занято (то есть уже существует очередь необработанных пакетов IRP), помещает очередной пакет с состоянием 'pending' в очередь необработанных запросов. Таким образом, Диспетчер ввода/вывода выполняет сериализацию IRP запросов, вызывая рестарт ввода/вывода только по окончании обработки предыдущего запроса.

Как правило, процедура StartIo организует внутреннюю сортировку поступающих в нее IRP пакетов по кодам IRP_MJ_Xxx (например, обычным оператором 'switch'). Завершается работа StartIo тем, что текущий IRP пакет помечается как обработанный вызовом IoCompleteRequest (таблица 9.10), после чего выполняется вызов IoStartNextPacket, что побуждает Диспетчера ввода/вывода вызывать StartIo снова — для следующего из оставшихся в очереди IRP пакетов.

В том случае, если невозможность обработки запроса была обусловлена временной неработоспособностью устройства, то запуск обработки очереди накопившихся IRP пакетов может быть выполнен следующим образом при участии ISR процедуры. Устройство сигнализирует о способности к работе прерыванием, ISR процедура обработки прерывания планирует запуск соответствующей DPC процедуры, которая делает вызов IoStartNextPacket (поскольку он может быть сделан только программным кодом уровня DISPATCH_LEVEL). Этот вызов и запускает обработку помещенных в очередь IRP пакетов, побуждая Диспетчера ввода/вывода вызывать процедуру StartIo. Аналогично, вместо автоматического запуска обработки следующего IRP пакета в конце процедуры StartIo, как было указано выше, можно запускать следующую операцию ввода/вывода также по сигналу прерывания от устройства.

Размещение необработанных пакетов в очереди производится на принципах FIFO, то есть в конец очереди. Однако можно изменить это правило, задавая определенные значения параметра Key в вызове IoStartPacket. Изменить порядок извлечения очередного необработанного пакета из очереди можно, задавая определенные значения параметра Key в вызове IoStartNextPacketByKey.

Процедура StartIo регистрируется во время работы DriverEntry записью ее адреса в поле DriverStartIo в структуре объекта драйвера (что похоже на регистрацию процедуры AddDevice).

Процедура обслуживания прерываний

Процедура обслуживания прерываний (Interrupt Service Routine, ISR), входящая в набор процедур драйвера, вызывается диспетчером прерываний ядра (Kernel's interrupt dispatcher) всякий раз, когда устройство генерирует сигнал прерывания. На этой процедуре лежит обязанность полного обслуживания аппаратного прерывания.

Собственно в ISR процедуре драйвера должна быть реализована самая минимальная обработка создавшейся ситуации. Если дополнительная, требующая больших временных затрат обработка прерывания требуется по логике работы устройства, то следует прибегнуть к использованию механизма DPC (отложенных процедурных вызовов), то есть запланировать отложенный процедурный вызов в текущей процедуре обработки прерываний (ISR). После этого, остаток работы ISR процедуры можно завершить на уровне IRQL ниже уровня аппаратных прерываний (DIRQL), понизив приоритет выполняемого кода данных комплексом мер.

Процедуры DPC

Драйвер может предоставлять любое число DPC процедур, которые выполняют полностью или завершают работу (начатую в других процедурах драйвера) по взаимодействию с обслуживаемым им устройством. В их функции может входить освобождение системных ресурсов, сообщения об ошибках, пометка запроса на ввод/вывод как завершенного, запуск (старт) новой операции устройства.