Gerenciando prioridades de hardware
O IRQL no qual uma rotina de driver é executada determina quais rotinas de suporte ao driver no modo kernel ele pode chamar. Por exemplo, algumas rotinas de suporte ao driver exigem que o chamador esteja em execução em IRQL = DISPATCH_LEVEL. Outros não poderão ser chamados com segurança se o chamador estiver em execução em qualquer IRQL maior que PASSIVE_LEVEL.
A seguir está uma lista de IRQLs nas quais as rotinas de driver padrão mais comumente implementadas são chamadas. Os IRQLs são listados da prioridade mais baixa para a mais alta.
PASSIVE_LEVEL
Interrupções mascaradas — nenhuma.
Rotinas de driver chamadas em PASSIVE_LEVEL — DriverEntry, AddDevice, Reinitialize, Unload routines, most dispatch routines, driver-created threads, worker-thread callbacks.
APC_LEVEL
Interrupções Mascaradas – APC_LEVEL interrupções são mascaradas.
Rotinas de driver chamadas em APC_LEVEL — Algumas rotinas de expedição (consulte Rotinas de expedição e IRQLs).
DISPATCH_LEVEL
Interrupções mascaradas — DISPATCH_LEVEL e APC_LEVEL interrupções são mascaradas. Interrupções de dispositivo, relógio e falha de energia podem ocorrer.
Rotinas de driver chamadas em DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (enquanto mantém o bloqueio de rotação de cancelamento), rotinas DpcForIsr, CustomTimerDpc, CustomDpc .
DIRQL
Interrupções Mascaradas – Todas as interrupções em IRQL <= DIRQL do objeto de interrupção do driver. Interrupções de dispositivo com um valor DIRQL mais alto podem ocorrer, juntamente com interrupções de falha de relógio e energia.
Rotinas de driver chamadas em DIRQL — rotinas InterruptService, SynchCritSection .
A única diferença entre APC_LEVEL e PASSIVE_LEVEL é que um processo em execução em APC_LEVEL não pode obter interrupções de APC. Mas ambas as IRQLs implicam um contexto de thread e ambas implicam que o código pode ser paginado.
Os drivers de nível mais baixo processam IRPs durante a execução em um dos três IRQLs:
PASSIVE_LEVEL, sem interrupções mascaradas no processador, nas rotinas de expedição do driver
As rotinas DriverEntry, AddDevice, Reinitialize e Unload também são executadas em PASSIVE_LEVEL, assim como os threads do sistema criados pelo driver.
DISPATCH_LEVEL, com interrupções de DISPATCH_LEVEL e APC_LEVEL mascaradas no processador, na rotina StartIo
As rotinas AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (embora contenha o bloqueio de rotação de cancelamento) e CustomTimerDpc também são executadas em DISPATCH_LEVEL, assim como as rotinas DpcForIsr e CustomDpc.
DIRQL (IRQL do dispositivo), com todas as interrupções em menos ou igual ao SynchronizeIrql dos objetos de interrupção do driver mascarados no processador, nas rotinas ISR e SynchCritSection
A maioria dos drivers de nível superior processa IRPs durante a execução em um dos dois IRQLs:
PASSIVE_LEVEL, sem interrupções mascaradas no processador, nas rotinas de expedição do driver
As rotinas DriverEntry, Reinitialize, AddDevice e Unload também são executadas em PASSIVE_LEVEL, assim como quaisquer threads do sistema criados pelo driver ou rotinas de retorno de chamada de thread de trabalho ou drivers do sistema de arquivos.
DISPATCH_LEVEL, com DISPATCH_LEVEL e APC_LEVEL interrupções mascaradas no processador, na rotina IoCompletion do driver
As rotinas IoTimer, Cancel e CustomTimerDpc também são executadas em DISPATCH_LEVEL.
Em algumas circunstâncias, drivers intermediários e de nível mais baixo de dispositivos de armazenamento em massa são chamados em APC_LEVEL IRQL. Em particular, isso pode ocorrer em uma falha de página para a qual um driver do sistema de arquivos envia uma solicitação IRP_MJ_READ para drivers inferiores.
A maioria das rotinas de driver padrão é executada em um IRQL que permite que eles simplesmente chamem as rotinas de suporte apropriadas. Por exemplo, um driver de dispositivo deve chamar AllocateAdapterChannel durante a execução no IRQL DISPATCH_LEVEL. Como a maioria dos drivers de dispositivo chama essas rotinas de uma rotina StartIo , geralmente eles já estão em execução em DISPATCH_LEVEL.
Observe que um driver de dispositivo que não tem rotina StartIo porque ele configura e gerencia suas próprias filas de IRPs não está necessariamente em execução em DISPATCH_LEVEL IRQL quando deve chamar AllocateAdapterChannel. Esse driver deve aninhar sua chamada para AllocateAdapterChannel entre chamadas para KeRaiseIrql e KeLowerIrql para que ele seja executado no IRQL necessário quando chamar AllocateAdapterChannel e restaurar o IRQL original quando a rotina de chamada recuperar o controle.
Ao chamar rotinas de suporte ao driver, esteja ciente do seguinte.
Chamar KeRaiseIrql com um valor NewIrql de entrada menor que o IRQL atual causa um erro fatal. Chamar KeLowerIrql , exceto para restaurar o IRQL original (ou seja, após uma chamada para KeRaiseIrql) também causa um erro fatal.
Durante a execução em IRQL >= DISPATCH_LEVEL, chamar KeWaitForSingleObject ou KeWaitForMultipleObjects para objetos dispatcher definidos por kernel para aguardar um intervalo diferente de zero causa um erro fatal.
As únicas rotinas de driver que podem aguardar com segurança eventos, semáforos, mutexes ou temporizadores a serem definidos para o estado sinalizado são aquelas executadas em um contexto de thread não-bitrário no IRQL PASSIVE_LEVEL, como threads criados pelo driver, as rotinas DriverEntry e Reinitialize ou rotinas de expedição para operações de E/S inerentemente síncronas (como a maioria das solicitações de controle de E/S do dispositivo).
Mesmo em execução no IRQL PASSIVE_LEVEL, o código de driver paginável não deve chamar KeSetEvent, KeReleaseSemaphore ou KeReleaseMutex com o parâmetro wait de entrada definido como TRUE. Essa chamada pode causar uma falha fatal na página.
Qualquer rotina em execução em maior que o IRQL APC_LEVEL não pode alocar memória do pool de páginas nem acessar a memória no pool de páginas com segurança. Se uma rotina em execução no IRQL maior que APC_LEVEL causar uma falha de página, será um erro fatal.
Um driver deve estar em execução no IRQL DISPATCH_LEVEL quando chama KeAcquireSpinLockAtDpcLevel e KeReleaseSpinLockFromDpcLevel.
Um driver pode estar em execução em IRQL <= DISPATCH_LEVEL quando chama KeAcquireSpinLock , mas deve liberar esse bloqueio de rotação chamando KeReleaseSpinLock. Em outras palavras, é um erro de programação liberar um bloqueio de rotação adquirido com KeAcquireSpinLock chamando KeReleaseSpinLockFromDpcLevel.
Um driver não deve chamar KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock ou KeReleaseSpinLock durante a execução no IRQL > DISPATCH_LEVEL.
Chamar uma rotina de suporte que usa um bloqueio de rotação, como uma rotina Xxx ExInterlocked, eleva o IRQL no processador atual para DISPATCH_LEVEL ou para DIRQL se o chamador ainda não estiver em execução em um IRQL gerado.
O código do driver executado no IRQL > PASSIVE_LEVEL deve ser executado o mais rápido possível. Quanto maior o IRQL em que uma rotina é executada, mais importante é que o bom desempenho geral ajuste essa rotina para executar o mais rápido possível. Por exemplo, qualquer driver que chame KeRaiseIrql deve fazer a chamada recíproca para KeLowerIrql assim que possível.
Para obter mais informações sobre como determinar prioridades, consulte o white paper Agendamento, Contexto de Thread e IRQL .