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

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

diagram illustrating waiting for an event object.

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

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

  • SynchronizationEvent

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

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

  • NotificationEvent

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

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

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

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

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

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

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

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

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

Примите во внимание следующие рекомендации по настройкеtheWaitparametertoKeSetEvent:

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

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

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

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

При вызове описанных выше процедур поддержки учитывайте следующее руководство.

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