Gestione delle priorità hardware

IrQL in corrispondenza del quale viene eseguita una routine driver determina quali driver in modalità kernel supportano le routine che può chiamare. Ad esempio, alcune routine di supporto driver richiedono che il chiamante sia in esecuzione in IRQL = DISPATCH_LEVEL. Altri non possono essere chiamati in modo sicuro se il chiamante è in esecuzione in un IRQL superiore a PASSIVE_LEVEL.

Di seguito è riportato un elenco di IRQLs in cui vengono chiamate le routine del driver standard più comunemente implementate. I valori IRQLs sono elencati dalla priorità più bassa alla più alta.

PASSIVE_LEVEL
Interruzioni mascherate : nessuna.

Routine driver chiamate all'indirizzo PASSIVE_LEVEL — DriverEntry, AddDevice, Reinizialize, Scarica routine, la maggior parte delle routine dispatch, thread creati dal driver, callback del thread di lavoro.

APC_LEVEL
Interruzioni disattivate : APC_LEVEL interruzioni vengono mascherate.

Routine driver chiamate all'indirizzo APC_LEVEL : alcune routine dispatch (vedere Routine dispatch e IRQL).

DISPATCH_LEVEL
Interruzioni disattivate : DISPATCH_LEVEL e APC_LEVEL interruzioni vengono mascherate. Possono verificarsi interruzioni di dispositivo, orologio e interruzioni dell'alimentazione.

Routine driver chiamate all'indirizzo DISPATCH_LEVEL : StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (tenendo premuto il blocco di selezione annulla), DpcForIsr, CustomTimerDpc, routine CustomDpc .

DIRQL
Interrupts Masked Off : tutti gli interrupt in IRQL <= DIRQL dell'oggetto interrupt del driver. Possono verificarsi interruzioni del dispositivo con un valore DIRQL superiore, insieme a interruzioni di clock e interruzioni dell'alimentazione.

Routine driver chiamate a DIRQL - InterruptService, routine SynchCritSection .

L'unica differenza tra APC_LEVEL e PASSIVE_LEVEL è che un processo in esecuzione in APC_LEVEL non può ottenere interrupt APC. Ma entrambi gli IRQL implicano un contesto di thread e entrambi implicano che il codice può essere risolto tramite page out.

I driver di livello più basso elaborano i runtime di integrazione durante l'esecuzione in uno dei tre tipi di runtime di integrazione:

  • PASSIVE_LEVEL, senza interruzioni mascherate sul processore, nelle routine Dispatch del driver

    Anche le routine DriverEntry, AddDevice, Reinitialize e Unload vengono eseguite in PASSIVE_LEVEL, come tutti i thread di sistema creati dal driver.

  • DISPATCH_LEVEL, con DISPATCH_LEVEL e APC_LEVEL interrompe l'interruzione del processore, nella routine StartIo

    AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (mentre contiene il blocco di selezione annulla) e le routine CustomTimerDpc vengono eseguite anche in DISPATCH_LEVEL, così come le routine DpcForIsr e CustomDpc .

  • DEVICE IRQL (DIRQL), con tutte le interruzioni minori o uguali all'oggetto SynchronizeIrql degli oggetti interrupt del driver mascherati sul processore, nelle routine ISR e SynchCritSection

La maggior parte dei driver di livello superiore elabora i runtime di integrazione durante l'esecuzione in uno dei due tipi di runtime di integrazione:

  • PASSIVE_LEVEL, senza interruzioni mascherate sul processore, nelle routine di invio del driver

    Anche le routine DriverEntry, Reinitialize, AddDevice e Unload vengono eseguite in PASSIVE_LEVEL, così come tutti i thread di sistema creati dal driver o routine di callback del thread di lavoro o driver di file system.

  • DISPATCH_LEVEL, con DISPATCH_LEVEL e APC_LEVEL interrompe l'interruzione del processore, nelle routine IoCompletion del driver

    Anche le routine IoTimer, Cancel e CustomTimerDpc vengono eseguite in DISPATCH_LEVEL.

In alcune circostanze, i driver intermedi e di livello più basso dei dispositivi di archiviazione di massa vengono chiamati a IRQL APC_LEVEL. In particolare, ciò può verificarsi in caso di errore di pagina per cui un driver del file system invia una richiesta di IRP_MJ_READ a driver inferiori.

La maggior parte delle routine del driver standard viene eseguita in un runtime di integrazione che consente di chiamare semplicemente le routine di supporto appropriate. Ad esempio, un driver di dispositivo deve chiamare AllocateAdapterChannel durante l'esecuzione in IRQL DISPATCH_LEVEL. Poiché la maggior parte dei driver di dispositivo chiama queste routine da una routine StartIo , in genere vengono eseguite in DISPATCH_LEVEL già.

Si noti che un driver di dispositivo senza routine StartIo perché configura e gestisce le proprie code di runtime di integrazione non è necessariamente in esecuzione in DISPATCH_LEVEL IRQL quando deve chiamare AllocateAdapterChannel. Tale driver deve annidare la chiamata a AllocateAdapterChannel tra le chiamate a KeRaiseIrql e KeLowerIrql in modo che venga eseguita nel runtime di integrazione richiesto quando chiama AllocateAdapterChannel e ripristina l'IRQL originale quando la routine chiamante recupera il controllo.

Quando si chiamano le routine di supporto del driver, tenere presente quanto segue.

  • La chiamata a KeRaiseIrql con un valore NewIrql di input minore dell'IRQL corrente genera un errore irreversibile. La chiamata a KeLowerIrql , ad eccezione del ripristino dell'IRQL originale ( ovvero dopo una chiamata a KeRaiseIrql) causa anche un errore irreversibile.

  • Durante l'esecuzione in IRQL >= DISPATCH_LEVEL, la chiamata a KeWaitForSingleObject o KeWaitForMultipleObjects per gli oggetti dispatcher definiti dal kernel per attendere un intervallo diverso da zero causa un errore irreversibile.

  • Le uniche routine driver che possono attendere in modo sicuro gli eventi, i semafori, i mutex o i timer da impostare sullo stato segnalato sono quelli eseguiti in un contesto di thread non arbiverso in irQL PASSIVE_LEVEL, ad esempio thread creati dal driver, DriverEntry e Reinizializzare routine o routine dispatch per operazioni di I/O intrinsecamente sincrone (ad esempio la maggior parte delle richieste di controllo di I/O del dispositivo).

  • Anche durante l'esecuzione in IRQL PASSIVE_LEVEL, il codice del driver pageable non deve chiamare KeSetEvent, KeReleaseSemaphore o KeReleaseMutex con il parametro Wait di input impostato su TRUE. Una chiamata di questo tipo può causare un errore irreversibile della pagina.

  • Qualsiasi routine in esecuzione con un numero maggiore di APC_LEVEL IRQL non può allocare memoria dal pool di paging né accedere alla memoria nel pool di paging in modo sicuro. Se una routine in esecuzione in IRQL maggiore di APC_LEVEL causa un errore di pagina, si tratta di un errore irreversibile.

  • Un driver deve essere in esecuzione in IRQL DISPATCH_LEVEL quando chiama KeAcquireSpinLockAtDpcLevel e KeReleaseSpinLockFromDpcLevel.

    Un driver può essere eseguito in IRQL <= DISPATCH_LEVEL quando chiama KeAcquireSpinLock , ma deve rilasciare tale blocco spin chiamando KeReleaseSpinLock. In altre parole, si tratta di un errore di programmazione per rilasciare un blocco spin acquisito con KeAcquireSpinLock chiamando KeReleaseSpinLockFromDpcLevel.

    Un driver non deve chiamare KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock o KeReleaseSpinLock durante l'esecuzione in IRQL > DISPATCH_LEVEL.

  • Chiamando una routine di supporto che usa un blocco spin, ad esempio una routine ExInterlocked Xxx, genera IRQL sul processore corrente per DISPATCH_LEVEL o a DIRQL se il chiamante non è già in esecuzione in un IRQL generato.

  • Il codice driver eseguito in IRQL > PASSIVE_LEVEL deve essere eseguito il più rapidamente possibile. Maggiore è l'IRQL in cui viene eseguita una routine, più importante è che le prestazioni complessive siano ottimali per ottimizzare la routine da eseguire il più rapidamente possibile. Ad esempio, qualsiasi driver che chiama KeRaiseIrql deve effettuare la chiamata reciproca a KeLowerIrql non appena può.

Per altre informazioni sulla determinazione delle priorità, vedere il white paper Pianificazione, Contesto thread e IRQL .