Compartilhar via


Proteção para Run-Down

Os drivers no modo kernel podem usar a proteção de execução para acessar com segurança objetos na memória do sistema compartilhado que são criados e excluídos por outro driver no modo kernel.

Um objeto é considerado esgotado se todos os acessos pendentes ao objeto forem concluídos e nenhuma solicitação nova para acessar o objeto for concedida. Por exemplo, um objeto compartilhado pode precisar ser finalizado para que ele possa ser excluído e substituído por um novo objeto.

O driver que possui o objeto compartilhado pode permitir que outros drivers adquiram e liberem a proteção de esgotamento no objeto. Quando a proteção contra encerramento está em vigor, um driver que não seja o proprietário pode acessar o objeto sem risco de que este seja excluído pelo proprietário antes da conclusão do acesso. Antes do acesso ser iniciado, o driver de acesso solicita proteção de execução no objeto. Para um objeto de longa duração, essa solicitação quase sempre é concedida. Após a conclusão do acesso, o driver libera sua proteção de desligamento gradual previamente adquirida no objeto.

Rotinas de proteção de redução primária

Para começar a compartilhar um objeto, o driver que possui o objeto chama a rotina ExInitializeRundownProtection para inicializar a proteção de run-down no objeto. Após essa chamada, outros drivers que acessam o objeto podem adquirir e liberar a proteção de execução no objeto.

Um driver que acessa o objeto compartilhado chama a rotina ExAcquireRundownProtection para solicitar proteção de execução no objeto. Depois que o acesso for concluído, esse driver chamará a rotina ExReleaseRundownProtection para liberar a proteção run-down no objeto.

Se o driver proprietário determinar que o objeto compartilhado deve ser excluído, esse driver aguardará para excluir o objeto até que todos os acessos pendentes do objeto sejam concluídos.

Em preparação para excluir o objeto compartilhado, o driver proprietário chama a rotina ExWaitForRundownProtectionRelease para aguardar a finalização do objeto. Durante essa chamada, ExWaitForRundownProtectionRelease aguarda que todas as instâncias de proteção de esgotamento concedidas anteriormente no objeto sejam liberadas, mas impede que novas solicitações de proteção de esgotamento no objeto sejam concedidas. Depois que o último acesso protegido for concluído e todas as instâncias de proteção de execução forem liberadas, ExWaitForRundownProtectionRelease retornará e o driver proprietário poderá excluir o objeto com segurança.

ExWaitForRundownProtectionRelease bloqueia a execução do thread do driver de chamada até que todos os drivers que mantêm a proteção em execução no objeto compartilhado liberem essa proteção. Para impedir que ExWaitForRundownProtectionRelease bloqueie a execução por períodos excessivamente longos, as threads dos drivers que acessam o objeto compartilhado devem evitar serem suspensas enquanto mantêm a proteção contra encerramento no objeto. Por esse motivo, os drivers de acesso devem chamar ExAcquireRundownProtection e ExReleaseRundownProtection em uma região crítica ou região protegida ou durante a execução em IRQL = APC_LEVEL.

Usos para proteção contra descarga

A proteção de execução é útil para fornecer acesso a um objeto compartilhado quase sempre disponível, mas que pode ocasionalmente precisar ser excluído e substituído. Os drivers que acessam dados ou que chamam rotinas neste objeto não devem tentar acessar o objeto depois que ele é excluído. Caso contrário, esses acessos inválidos podem causar comportamento imprevisível, corrupção de dados ou até mesmo falha no sistema.

Por exemplo, um driver antivírus normalmente permanece carregado na memória quando o sistema operacional está em execução. Ocasionalmente, esse driver pode precisar ser descarregado e substituído por uma versão atualizada do driver. Outros drivers enviam solicitações de E/S para o driver antivírus a fim de acessar os dados e rotinas desse driver. Antes de enviar uma solicitação de E/S, um componente do kernel, como um gerenciador de filtros do sistema de arquivos, pode adquirir proteção de execução para se proteger contra o descarregamento prematuro do driver antivírus enquanto ele lida com a solicitação de E/S. Após a conclusão da solicitação de E/S, a proteção contra queda de atividade pode ser liberada.

A proteção de execução não serializa os acessos a um objeto compartilhado. Se dois ou mais drivers de acesso puderem manter simultaneamente a proteção de execução em um objeto e os acessos ao objeto precisarem ser serializados, algum outro mecanismo, como um bloqueio de exclusão mútua, deverá ser usado para serializar os acessos.

A estrutura EX_RUNDOWN_REF

Uma estrutura EX_RUNDOWN_REF controla o status da proteção em execução em um objeto compartilhado. Essa estrutura é opaca para drivers. As rotinas de proteção contra desativação fornecidas pelo sistema usam essa estrutura para contar o número de instâncias de proteção contra desativação que estão atualmente em vigor no objeto. Essas rotinas também usam essa estrutura para rastrear se o objeto está desativado ou em processo de desativação.

Para começar a compartilhar um objeto, o driver que possui o objeto chama ExInitializeRundownProtection para inicializar a estrutura de EX_RUNDOWN_REF associada ao objeto. Após a inicialização, o driver proprietário pode disponibilizar essa estrutura para outros drivers que exigem acesso ao objeto. Os drivers de acesso passam essa estrutura como um parâmetro para as chamadas ExAcquireRundownProtection e ExReleaseRundownProtection que adquirem e liberam a proteção de run-down no objeto. O driver proprietário passa essa estrutura como um parâmetro para a chamada ExWaitForRundownProtectionRelease, que aguarda o término do processamento do objeto, permitindo que ele possa ser excluído com segurança.

Comparação com trancas

A proteção de execução é uma das várias maneiras de garantir o acesso seguro a um objeto compartilhado. Outra abordagem é usar um bloqueio de software de exclusão mútua. Se um driver exigir acesso a um objeto que atualmente está bloqueado por outro driver, o primeiro driver deverá aguardar o segundo driver liberar o bloqueio. No entanto, adquirir e liberar bloqueios pode se tornar um gargalo de desempenho, e os bloqueios podem consumir grandes quantidades de memória. Se usado incorretamente, os bloqueios poderão fazer com que os drivers que competem pelos mesmos objetos compartilhados fiquem em deadlock. Os esforços para detectar e evitar deadlocks normalmente exigem o desvio de recursos de computação substanciais.

Ao contrário dos bloqueios, a proteção contra esgotamento tem requisitos relativamente leves de tempo de processamento e memória. Uma contagem de referência simples está associada ao objeto para garantir que a exclusão do objeto seja adiada até que todos os acessos pendentes do objeto sejam concluídos. Com essa abordagem, instruções de hardware atômicas e intertravadas podem ser usadas em vez de bloqueios de software de exclusão mútua para garantir o acesso seguro a um objeto. As chamadas para adquirir e liberar a proteção de execução são normalmente rápidas. Os benefícios do uso de um mecanismo leve, como a proteção de run-down, podem ser significativos para um objeto compartilhado que tem uma vida útil longa e é compartilhado entre muitos drivers.

Outras rotinas de proteção em execução

Diversas outras rotinas de proteção prontas para execução estão disponíveis, além daquelas que foram mencionadas anteriormente. Essas rotinas extras podem ser usadas por alguns drivers.

A rotina ExReInitializeRundownProtection permite que uma estrutura de EX_RUNDOWN_REF usada anteriormente seja associada a um novo objeto e inicializa a proteção de run-down nesse objeto.

A rotina ExRundownCompleted atualiza a estrutura EX_RUNDOWN_REF para indicar que a execução do objeto associado foi concluída.

As rotinas ExAcquireRundownProtectionEx e ExReleaseRundownProtectionEx são semelhantes a ExAcquireRundownProtection e ExReleaseRundownProtection. Essas quatro rotinas incrementam ou decrementam a contagem das instâncias de proteção em execução que estão em vigor em um objeto compartilhado. Enquanto ExAcquireRundownProtection e ExReleaseRundownProtection incrementam e decrementam essa contagem por um, ExAcquireRundownProtectionEx e ExReleaseRundownProtectionEx incrementam e decrementam a contagem por valores arbitrários.

Proteção de desativação com reconhecimento de cache

Uma referência de rundown é uma estrutura de dados compacta e veloz, mas pode provocar contenção de cache quando muitos processadores tentam acessar a referência simultaneamente. Isso pode afetar o desempenho e a escalabilidade do driver.

Para evitar esse problema, você pode usar uma referência de rundown com reconhecimento de cache para distribuir o acompanhamento de referência entre várias linhas de cache. Isso reduz a contenção de cache e melhora o desempenho do driver em computadores multiprocessadores.

Para usar uma referência de rundown com reconhecimento de cache, siga estas etapas:

  1. Crie um objeto EX_RUNDOWN_REF_CACHE_AWARE fazendo um dos seguintes procedimentos:
  2. Solicite proteção de rundown para o objeto antes de acessá-lo, chamando a rotina ExAcquireRundownProtectionCacheAware. Essa rotina retorna TRUE se a solicitação for concedida ou FALSE se o objeto estiver sendo desativado.
  3. Libere a proteção de rundown no objeto depois de acessá-la chamando a rotina ExReleaseRundownProtectionCacheAware .
  4. Aguarde até que o objeto seja finalizado antes de excluí-lo chamando a rotina ExWaitForRundownProtectionReleaseCacheAware. Essa rotina bloqueia o thread atual até que todas as instâncias de proteção de rundown no objeto sejam liberadas.
  5. Se o driver chamou ExAllocateCacheAwareRundownProtection anteriormente, deverá chamar ExFreeCacheAwareRundownProtection para liberar a referência de rundown.

Para reutilizar uma referência de rundown sensível ao cache, siga estas etapas:

  1. Depois de chamar ExWaitForRundownProtectionReleaseCacheAware, chame ExRundownCompletedCacheAware para indicar que a execução do objeto antigo foi concluída.
  2. Chame ExReInitializeRundownProtectionCacheAware para reinicializar a referência depois que o objeto associado for desativado.
  3. Agora, o driver pode novamente chamar ExAcquireRundownProtectionCacheAware.

Uma referência de rundown com reconhecimento de cache tem a vantagem de um melhor desempenho e escalabilidade em situações específicas, mas consome mais memória do que uma referência de rundown regular. Você deve considerar esse equilíbrio ao escolher entre os dois tipos de referências de rundown.