Определение и использование объекта события

Любой драйвер, использующий объект события, должен вызывать KeInitializeEvent, IoCreateNotificationEvent или IoCreateSynchronizationEvent , прежде чем он ожидает, задает, очищает или сбрасывает событие. На следующем рисунке показано, как драйвер с потоком может использовать объект события для синхронизации.

схема, иллюстрирующая ожидание объекта события.

Как показано на предыдущем рисунке, такой драйвер должен предоставлять хранилище для объекта события, который должен быть резидентом. Драйвер может использовать расширение устройства созданного драйвером объекта устройства, расширение контроллера, если он использует объект контроллера, или непагированные пулы, выделенные драйвером.

Когда драйвер вызывает KeInitializeEvent, он должен передать указатель на резидентное хранилище драйвера для объекта события. Кроме того, вызывающий объект должен указать начальное состояние объекта события (сигнализируется или не сигнализируется). Вызывающий объект также должен указать тип события, который может быть следующим:

  • SynchronizationEvent

    Если для события синхронизации задано состояние Signaled, один поток, ожидающий сброса события до Not-Signaled, становится доступным для выполнения, а состояние события автоматически сбрасывается в Состояние Без сигналов.

    Этот тип события иногда называют событием автоматической расчистки, так как его состояние Signaled автоматически сбрасывается при каждом выполнении ожидания.

  • NotificationEvent

    Если для события уведомления задано состояние Signaled , все потоки, ожидающие сброса события до Not-Signaled становятся доступными для выполнения, а событие остается в состоянии Signaled до тех пор, пока не произойдет явный сброс до Not-Signaled: вызов KeClearEvent или KeResetEvent с заданным указателем события .

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

Большинство драйверов, использующих объекты событий для ожидания завершения операции ввода-вывода, при вызове KeInitializeEvent задают тип входных данных NotificationEvent. Объект события, настроенный для irP, создаваемый драйвером с помощью IoBuildSynchronousFsdRequest или IoBuildDeviceIoControlRequest , почти всегда инициализируется как NotificationEvent , так как вызывающий объект будет ожидать уведомления о том, что его запрос был удовлетворен одним или несколькими драйверами более низкого уровня.

После инициализации драйвера его выделенный драйвером поток (если таковой имеется) и другие подпрограммы могут синхронизировать свои операции с событием. Например, драйвер с потоком, который управляет постановкой в очередь irP, например драйвер контроллера системной дискеты, может синхронизировать обработку IRP для события, как показано на предыдущем рисунке:

  1. Поток, который вывел из очереди IRP для обработки на устройстве, вызывает KeWaitForSingleObject с указателем на хранилище, предоставленное драйвером для инициализированного объекта события.

  2. Другие подпрограммы драйвера выполняют операции ввода-вывода устройства, необходимые для удовлетворения IRP. После завершения этих операций подпрограмма DpcForIsr драйвера вызывает KeSetEvent с указателем на объект события, определяемого драйвером повышения приоритета для потока (приращение, как показано на предыдущем рисунке), а для параметра Boolean Wait задано значение FALSE. Вызов KeSetEvent устанавливает для объекта события состояние Signaled, тем самым изменяя состояние ожидающего потока на готово.

  3. Ядро отправляет поток для выполнения, как только процессор станет доступен: то есть ни один другой поток с более высоким приоритетом в настоящее время не находится в состоянии готовности, и нет подпрограмм режима ядра, которые будут выполняться на более высоком irQL.

    Поток теперь может завершить IRP, если DpcForIsr еще не вызвал IoCompleteRequest с IRP и может вывести другой IRP для обработки на устройстве.

Вызов KeSetEvent с параметром Wait , равным TRUE , указывает на намерение вызывающего объекта немедленно вызвать подпрограмму поддержки KeWaitForSingleObject или KeWaitForMultipleObjects при возвращении из KeSetEvent.

Чтобы задать для параметра Wait значениеKeSetEvent, примите во внимание следующие рекомендации:

Выстраиваемый поток или подпрограмма драйвера, которая выполняется в DISPATCH_LEVEL IRQL < , никогда не должна вызывать KeSetEvent с параметром Wait , равным TRUE. Такой вызов вызывает неустранимую ошибку страницы, если вызывающий объект выходит между вызовами KeSetEvent и KeWaitForSingleObject или KeWaitForMultipleObjects.

Любая стандартная подпрограмма драйвера, которая выполняется в IRQL = DISPATCH_LEVEL не может ждать ненулевого интервала для любых объектов диспетчера без отключения системы. Однако такая подпрограмма может вызывать KeSetEvent при выполнении в IRQL меньше или равной DISPATCH_LEVEL.

Сводку по спискам IRQL, в которых выполняются стандартные подпрограммы драйверов, см. в разделе Управление приоритетами оборудования.

KeResetEvent возвращает предыдущее состояние данного события: было ли задано значение Signaled или нет при вызове KeResetEvent . KeClearEvent просто устанавливает для заданного события состояние Без сигналов.

Рассмотрим следующее руководство, когда следует вызывать предыдущие подпрограммы поддержки:

Для повышения производительности каждый драйвер должен вызывать KeClearEvent , если вызывающему объекту не требуется информация, возвращаемая KeResetEvent , чтобы определить, что делать дальше.