Definición y uso de un objeto de evento
Cualquier controlador que use un objeto de evento debe llamar a KeInitializeEvent, IoCreateNotificationEvent o IoCreateSynchronizationEvent antes de que espere, establezca, borre o restablezca el evento. En la ilustración siguiente se muestra cómo un controlador con un subproceso puede usar un objeto de evento para la sincronización.
Como se muestra en la ilustración anterior, este controlador debe proporcionar el almacenamiento para el objeto de evento, que debe ser residente. El controlador puede usar la extensión de dispositivo de un objeto de dispositivo creado por el controlador, la extensión del controlador si usa un objeto de controlador o un grupo no paginado asignado por el controlador.
Cuando el controlador llama a KeInitializeEvent, debe pasar un puntero al almacenamiento residente del controlador para el objeto de evento. Además, el autor de la llamada debe especificar el estado inicial (señalado o no señalado) para el objeto de evento. El autor de la llamada también debe especificar el tipo de evento , que puede ser cualquiera de los siguientes:
SynchronizationEvent
Cuando un evento de sincronización se establece en el estado Signaled, un único subproceso que espera a que el evento se restablezca a Not-Signaled se convierte en apto para su ejecución y el estado del evento se restablece automáticamente a Not-Signaled.
A veces, este tipo de evento se denomina evento de autoclear, porque su estado Señalizado se restablece automáticamente cada vez que se satisface una espera.
NotificationEvent
Cuando un evento de notificación se establece en el estado Signaled, todos los subprocesos que estaban esperando que el evento se restablezca a Not-Signaled sean aptos para su ejecución y el evento permanece en estado Signaled hasta que se produzca un restablecimiento explícito a Not-Signaled: es decir, hay una llamada a KeClearEvent o KeResetEvent con el puntero de evento especificado.
Algunos controladores intermedios o de dispositivo tienen un único subproceso dedicado al controlador, por ejemplo, un conjunto de subprocesos que podrían sincronizar sus operaciones esperando un evento que protege un recurso compartido.
La mayoría de los controladores que usan objetos de evento para esperar a que se complete una operación de E/S establezca el tipo de entrada en NotificationEvent cuando llamen a KeInitializeEvent. Un objeto de evento configurado para IRP que crea un controlador con IoBuildSynchronousFsdRequest o IoBuildDeviceIoControlRequest casi siempre se inicializa como notificationEvent porque el autor de la llamada esperará al evento para la notificación de que su solicitud ha sido satisfecho por uno o varios controladores de nivel inferior.
Una vez que el controlador se haya inicializado, su subproceso dedicado al controlador, si existe, y otras rutinas, pueden sincronizar sus operaciones en el evento. Por ejemplo, un controlador con un subproceso que administra la puesta en cola de IRP, como el controlador del controlador del disquete del sistema, podría sincronizar el procesamiento de IRP en un evento, como se muestra en la ilustración anterior:
El subproceso, que ha desqueuado un IRP para su procesamiento en el dispositivo, llama a KeWaitForSingleObject con un puntero al almacenamiento proporcionado por el controlador para el objeto de evento inicializado.
Otras rutinas de controlador llevan a cabo las operaciones de E/S necesarias para satisfacer el IRP y, cuando estas operaciones se completan, la rutina DpcForIsr del controlador llama a KeSetEvent con un puntero al objeto de evento, un aumento de prioridad determinado por el controlador para el subproceso (Incremento, como se muestra en la ilustración anterior) y una espera booleana establecida en FALSE. Al llamar a KeSetEvent , el objeto de evento se establece en el estado Signaled, cambiando así el estado del subproceso en espera a listo.
El kernel envía el subproceso para su ejecución tan pronto como un procesador esté disponible: es decir, ningún otro subproceso con una prioridad más alta está actualmente en estado listo y no hay rutinas en modo kernel que se ejecuten en un IRQL superior.
El subproceso ahora puede completar el IRP si el DpcForIsr no ha llamado a IoCompleteRequest con el IRP ya y puede quitar de la cola otro IRP que se va a procesar en el dispositivo.
Al llamar a KeSetEvent con el parámetro Wait establecido en TRUE , se indica la intención del autor de la llamada de llamar inmediatamente a una rutina de soporte de KeWaitForSingleObject o KeWaitForMultipleObjects devuelta desde KeSetEvent.
Tenga en cuenta las siguientes directrices para establecer el parámetro WaitenKeSetEvent:
Una rutina de controlador paginable o subproceso paginable que se ejecuta en IRQL < DISPATCH_LEVEL nunca debe llamar a KeSetEvent con el parámetro Wait establecido en TRUE. Esta llamada provoca un error grave en la página si el autor de la llamada se pagina entre las llamadas a KeSetEvent y KeWaitForSingleObject o KeWaitForMultipleObjects.
Cualquier rutina de controlador estándar que se ejecute en IRQL = DISPATCH_LEVEL no puede esperar un intervalo distinto de cero en cualquier objeto de distribuidor sin bajar el sistema. Sin embargo, esta rutina puede llamar a KeSetEvent mientras se ejecuta en un IRQL menor o igual que DISPATCH_LEVEL.
Para obtener un resumen de las IRQL en las que se ejecutan las rutinas de controlador estándar, consulte Administración de prioridades de hardware.
KeResetEvent devuelve el estado anterior de un evento determinado: si se estableció en Signaled o no cuando se produjo la llamada a KeResetEvent . KeClearEvent simplemente establece el estado del evento dado en Not-Signaled.
Tenga en cuenta las siguientes directrices para cuándo llamar a las rutinas de soporte técnico anteriores:
Para mejorar el rendimiento, cada controlador debe llamar a KeClearEvent a menos que el autor de la llamada necesite la información devuelta por KeResetEvent para determinar qué hacer a continuación.