Compartilhar via


Definindo e usando um objeto event

Qualquer driver que usa um objeto de evento deve chamar KeInitializeEvent, IoCreateNotificationEvent ou IoCreateSynchronizationEvent antes de aguardar, definir, limpar ou redefinir o evento. A figura a seguir ilustra como um driver com um thread pode usar um objeto de evento para sincronização.

diagrama ilustrando a espera de um objeto de evento.

Como mostra a figura anterior, esse driver deve fornecer o armazenamento para o objeto de evento, que deve ser residente. O driver pode usar a extensão de dispositivo de um objeto de dispositivo criado pelo driver, a extensão do controlador se ele usar um objeto de controlador ou um pool nãopagado alocado pelo driver.

Quando o driver chama KeInitializeEvent, ele deve passar um ponteiro para o armazenamento residente do driver para o objeto de evento. Além disso, o chamador deve especificar o estado inicial (sinalizado ou não sinalizado) para o objeto de evento. O chamador também deve especificar o tipo de evento, que pode ser um dos seguintes:

  • SynchronizationEvent

    Quando um evento de sincronização é definido como o estado Sinalizado, um único thread que aguarda a redefinição do evento para Not-Signaled torna-se qualificado para execução e o estado do evento é redefinido automaticamente para Não Sinalizado.

    Esse tipo de evento às vezes é chamado de evento de autoclearing, porque seu estado Sinalizado é redefinido automaticamente sempre que uma espera é atendida.

  • NotificationEvent

    Quando um evento de notificação é definido como o estado Sinalizado, todos os threads que aguardavam a redefinição do evento para Not-Signaled se tornam qualificados para execução e o evento permanece no estado Sinalizado até que ocorra uma redefinição explícita para Not-Signaled: ou seja, há uma chamada para KeClearEvent ou KeResetEvent com o ponteiro Event fornecido.

Poucos drivers de dispositivo ou intermediários têm um único thread dedicado ao driver, muito menos um conjunto de threads que podem sincronizar suas operações aguardando um evento que proteja um recurso compartilhado.

A maioria dos drivers que usam objetos de evento para aguardar a conclusão de uma operação de E/S define o Tipo de entrada como NotificationEvent quando eles chamam KeInitializeEvent. Um objeto de evento configurado para IRPs que um driver cria com IoBuildSynchronousFsdRequest ou IoBuildDeviceIoControlRequest é quase sempre inicializado como um NotificationEvent porque o chamador aguardará o evento para notificação de que sua solicitação foi atendida por um ou mais drivers de nível inferior.

Depois que o driver tiver se inicializado, seu thread dedicado ao driver, se houver, e outras rotinas poderão sincronizar suas operações no evento. Por exemplo, um driver com um thread que gerencia o enfileiramento de IRPs, como o driver do controlador de disquete do sistema, pode sincronizar o processamento irp em um evento, conforme mostrado na figura anterior:

  1. O thread, que desempou um IRP para processamento no dispositivo, chama KeWaitForSingleObject com um ponteiro para o armazenamento fornecido pelo driver para o objeto de evento inicializado.

  2. Outras rotinas de driver executam o dispositivo as operações de E/S necessárias para atender ao IRP e, quando essas operações são concluídas, a rotina DpcForIsr do driver chama KeSetEvent com um ponteiro para o objeto de evento, um aumento de prioridade determinado pelo driver para o thread (Incremento, conforme mostrado na figura anterior) e um Boolean Wait definido como FALSE. Chamar KeSetEvent define o objeto de evento para o estado Signaled, alterando assim o estado do thread de espera para pronto.

  3. O kernel despacha o thread para execução assim que um processador está disponível: ou seja, nenhum outro thread com prioridade mais alta está atualmente no estado pronto e não há rotinas de modo kernel a serem executadas em um IRQL mais alto.

    O thread agora poderá concluir o IRP se o DpcForIsr ainda não tiver chamado IoCompleteRequest com o IRP e puder desempacodificar outro IRP a ser processado no dispositivo.

Chamar KeSetEvent com o parâmetro Wait definido como TRUE indica a intenção do chamador de chamar imediatamente uma rotina de suporte KeWaitForSingleObject ou KeWaitForMultipleObjects no retorno de KeSetEvent.

Considere as seguintes diretrizes para definir o parâmetro WaitcomoKeSetEvent:

Um thread paginável ou rotina de driver paginável que é executado no IRQL < DISPATCH_LEVEL nunca deve chamar KeSetEvent com o parâmetro Wait definido como TRUE. Essa chamada causará uma falha fatal na página se o chamador for paginado entre as chamadas para KeSetEvent e KeWaitForSingleObject ou KeWaitForMultipleObjects.

Qualquer rotina de driver padrão executada em IRQL = DISPATCH_LEVEL não pode esperar por um intervalo diferente de zero em nenhum objeto dispatcher sem derrubar o sistema. No entanto, essa rotina pode chamar KeSetEvent durante a execução em um IRQL menor ou igual a DISPATCH_LEVEL.

Para obter um resumo das IRQLs nas quais as rotinas de driver padrão são executadas, consulte Gerenciando prioridades de hardware.

KeResetEvent retorna o estado anterior de um determinado Evento: se ele foi definido como Sinalizado ou não quando a chamada para KeResetEvent ocorreu. KeClearEvent simplesmente define o estado do evento especificado como não sinalizado.

Considere a seguinte diretriz para quando chamar as rotinas de suporte anteriores:

Para obter um melhor desempenho, cada driver deve chamar KeClearEvent , a menos que o chamador precise das informações retornadas por KeResetEvent para determinar o que fazer a seguir.