Protezione run-down
A partire da Windows XP, la protezione da run-down è disponibile per i driver in modalità kernel. I driver possono usare la protezione di run-down per accedere in modo sicuro agli oggetti nella memoria di sistema condivisa creati ed eliminati da un altro driver in modalità kernel.
Si dice che un oggetto venga eseguito inattivo se tutti gli accessi in sospeso dell'oggetto vengono completati e non verranno concesse nuove richieste di accesso all'oggetto. Ad esempio, potrebbe essere necessario eseguire un oggetto condiviso in modo che possa essere eliminato e sostituito con un nuovo oggetto.
Il driver proprietario dell'oggetto condiviso può consentire ad altri driver di acquisire e rilasciare la protezione dell'esecuzione nell'oggetto. Quando è attiva la protezione da run-down, un driver diverso dal proprietario può accedere all'oggetto senza rischi che il proprietario elimini l'oggetto prima del completamento dell'accesso. Prima dell'avvio dell'accesso, l'accesso alle richieste driver esegue la protezione inattiva nell'oggetto . Per un oggetto di lunga durata, questa richiesta viene quasi sempre concessa. Al termine dell'accesso, l'accesso al driver rilascia la protezione di run-down acquisita in precedenza nell'oggetto .
Routine di protezione da run-down principali
Per avviare la condivisione di un oggetto, il driver proprietario dell'oggetto chiama la routine ExInitializeRundownProtection per inizializzare la protezione dell'esecuzione nell'oggetto. Dopo questa chiamata, altri driver che accedono all'oggetto possono acquisire e rilasciare la protezione di run-down nell'oggetto .
Un driver che accede all'oggetto condiviso chiama la routine ExAcquireRundownProtection per richiedere la protezione di run-down sull'oggetto. Al termine dell'accesso, questo driver chiama la routine ExReleaseRundownProtection per rilasciare la protezione dell'esecuzione nell'oggetto.
Se il driver proprietario determina che l'oggetto condiviso deve essere eliminato, il driver attende di eliminare l'oggetto fino a quando non vengono completati tutti gli accessi in sospeso dell'oggetto.
In preparazione all'eliminazione dell'oggetto condiviso, il driver proprietario chiama la routine ExWaitForRundownProtectionRelease per attendere l'esecuzione dell'oggetto. Durante questa chiamata, ExWaitForRundownProtectionRelease attende il rilascio di tutte le istanze di protezione di run-down precedentemente concesse nell'oggetto, ma impedisce il rilascio di nuove richieste di protezione di run-down sull'oggetto. Al termine dell'ultimo accesso protetto e dopo il rilascio di tutte le istanze di protezione da run-down, ExWaitForRundownProtectionRelease restituisce e il driver proprietario può eliminare in modo sicuro l'oggetto.
ExWaitForRundownProtectionRelease blocca l'esecuzione del thread del driver chiamante fino a quando tutti i driver che mantengono la protezione di esecuzione nell'oggetto condiviso rilasciano questa protezione. Per impedire a ExWaitForRundownProtectionRelease di bloccare l'esecuzione per periodi eccessivamente lunghi, i thread driver che accedono all'oggetto condiviso devono evitare di essere sospesi mentre mantengono la protezione di run-down sull'oggetto. Per questo motivo, l'accesso ai driver deve chiamare ExAcquireRundownProtection e ExReleaseRundownProtection all'interno di un'area critica o un'area protetta oppure durante l'esecuzione in IRQL = APC_LEVEL.
Usa per la protezione da run-down
La protezione da run-down è particolarmente utile per fornire l'accesso a un oggetto condiviso quasi sempre disponibile, ma che può talvolta essere necessario eliminare e sostituire. I driver che accedono ai dati o che chiamano routine in questo oggetto non devono tentare di accedere all'oggetto dopo l'eliminazione. In caso contrario, questi accessi non validi potrebbero causare un comportamento imprevedibile, un danneggiamento dei dati o persino un errore di sistema.
Ad esempio, un driver antivirus rimane in genere caricato in memoria quando il sistema operativo è in esecuzione. In alcuni casi, questo driver potrebbe dover essere scaricato e sostituito con una versione aggiornata del driver. Altri driver inviano richieste di I/O al driver antivirus per accedere ai dati e alle routine in questo driver. Prima di inviare una richiesta di I/O, un componente kernel, ad esempio un gestore di filtri del file system, può acquisire la protezione dell'esecuzione per evitare lo scaricamento prematuro del driver antivirus mentre gestisce la richiesta di I/O. Al termine della richiesta di I/O, è possibile rilasciare la protezione da run-down.
La protezione di run-down non serializza gli accessi a un oggetto condiviso. Se due o più driver di accesso possono contenere contemporaneamente la protezione di run-down in un oggetto e gli accessi all'oggetto devono essere serializzati, è necessario utilizzare un altro meccanismo, ad esempio un blocco di esclusione reciproca, per serializzare gli accessi.
Struttura EX_RUNDOWN_REF
Una struttura EX_RUNDOWN_REF tiene traccia dello stato della protezione da run-down in un oggetto condiviso. Questa struttura è opaca per i driver. Le routine di protezione di esecuzione fornite dal sistema utilizzano questa struttura per contare il numero di istanze di protezione di run-down attualmente applicate all'oggetto. Queste routine usano anche questa struttura per tenere traccia se l'oggetto è inattivo o è in corso di esecuzione.
Per iniziare a condividere un oggetto, il driver proprietario dell'oggetto chiama ExInitializeRundownProtection per inizializzare la struttura EX_RUNDOWN_REF associata all'oggetto . Dopo l'inizializzazione, il driver proprietario può rendere disponibile questa struttura ad altri driver che richiedono l'accesso all'oggetto. I driver che accedono passano questa struttura come parametro alle chiamate ExAcquireRundownProtection e ExReleaseRundownProtection che acquisiscono e rilasciano la protezione per l'esecuzione dell'oggetto. Il driver proprietario passa questa struttura come parametro alla chiamata ExWaitForRundownProtectionRelease che attende che l'oggetto venga eseguito in modo che possa essere eliminato in modo sicuro.
Confronto con i blocchi
La protezione da run-down è uno dei diversi modi per garantire l'accesso sicuro a un oggetto condiviso. Un altro approccio consiste nell'usare un blocco software di esclusione reciproca. Se un driver richiede l'accesso a un oggetto attualmente bloccato da un altro driver, il primo driver deve attendere che il secondo driver rilasci il blocco. Tuttavia, l'acquisizione e il rilascio di blocchi possono diventare un collo di bottiglia delle prestazioni e i blocchi possono utilizzare grandi quantità di memoria. Se usati in modo non corretto, i blocchi potrebbero causare il deadlock dei driver che competono per gli stessi oggetti condivisi. Gli sforzi per rilevare ed evitare deadlock richiedono in genere la deviazione di risorse di calcolo sostanziali.
A differenza dei blocchi, la protezione da run-down presenta requisiti di tempo di elaborazione e memoria relativamente leggeri. Un semplice conteggio dei riferimenti è associato all'oggetto per garantire che l'eliminazione dell'oggetto venga posticipata fino al completamento di tutti gli accessi in sospeso dell'oggetto. Con questo approccio, le istruzioni hardware atomiche e interlocked possono essere usate invece di blocchi software di esclusione reciproca per garantire l'accesso sicuro a un oggetto. Le chiamate per acquisire e rilasciare la protezione di run-down sono in genere molto veloci. I vantaggi dell'uso di un meccanismo leggero, ad esempio la protezione da run-down, possono essere significativi per un oggetto condiviso che ha una lunga durata ed è condiviso tra molti driver.
Altre routine di protezione da run-down
Sono disponibili diverse altre routine di protezione da run-down, oltre a quelle menzionate in precedenza. Queste routine aggiuntive potrebbero essere usate da alcuni driver.
La routine ExReInitializeRundownProtection consente di associare una struttura di EX_RUNDOWN_REF utilizzata in precedenza a un nuovo oggetto e inizializza la protezione di run-down su questo oggetto.
La routine ExRundownCompleted aggiorna la struttura EX_RUNDOWN_REF per indicare che l'esecuzione dell'oggetto associato è stata completata.
Le routine ExAcquireRundownProtectionEx e ExReleaseRundownProtectionEx sono simili a ExAcquireRundownProtection e ExReleaseRundownProtection. Queste quattro routine incrementano o decrementano il conteggio delle istanze di protezione di run-down applicate a un oggetto condiviso. Mentre ExAcquireRundownProtection e ExReleaseRundownProtection incrementa e decrementa questo conteggio di uno, ExAcquireRundownProtectionEx e ExReleaseRundownProtectionEx incrementa e decrementa il conteggio in base a importi arbitrari.
Protezione con run-down compatibile con la cache
Un riferimento di rundown è una struttura di dati compatta e veloce, ma può causare conflitti nella cache quando molti processori tentano di acquisire il riferimento contemporaneamente. Ciò può influire sulle prestazioni e sulla scalabilità del driver.
Per evitare questo problema, è possibile usare un riferimento rundown compatibile con la cache per distribuire il rilevamento dei riferimenti tra più righe della cache. Ciò riduce la contesa della cache e migliora le prestazioni del driver nei computer multiprocessore.
Per usare un riferimento al rundown compatibile con la cache, seguire questa procedura:
- Creare un oggetto EX_RUNDOWN_REF_CACHE_AWARE eseguendo una delle operazioni seguenti:
- Chiama ExAllocateCacheAwareRundownProtection. Si noti che questa operazione si occupa dell'inizializzazione.
- In alternativa, per controllare l'allocazione di memoria, chiamare ExSizeOfRundownProtectionCacheAware, allocare un buffer delle dimensioni restituite, quindi passare tale buffer e dimensioni a ExInitializeRundownProtectionCacheAware.
- Richiedere la protezione del rundown sull'oggetto prima di accedervi chiamando la routine ExAcquireRundownProtectionCacheAware. Questa routine restituisce TRUE se la richiesta viene concessa o FAL edizione Standard se l'oggetto viene eseguito inattivo.
- Rilasciare la protezione del rundown sull'oggetto dopo l'accesso chiamando la routine ExReleaseRundownProtectionCacheAware.
- Attendere che l'oggetto venga eseguito prima di eliminarlo chiamando la routine ExWaitForRundownProtectionReleaseCacheAware. Questa routine blocca il thread corrente fino a quando non vengono rilasciate tutte le istanze di protezione rundown nell'oggetto .
- Se il driver denominato ExAllocateCacheAwareRundownProtection in precedenza, deve chiamare ExFreeCacheAwareRundownProtection per liberare il riferimento di rundown.
Per riutilizzare un riferimento a rundown compatibile con la cache, seguire questa procedura:
- Dopo aver chiamato ExWaitForRundownProtectionReleaseCacheAware, chiama ExRundownCompletedCacheAware per indicare che l'esecuzione dell'oggetto precedente è stata completata.
- Chiama ExReInitializeRundownProtectionCacheAware per reinizializzare il riferimento dopo l'esecuzione dell'oggetto associato.
- Ora il driver può chiamare nuovamente ExAcquireRundownProtectionCacheAware.
Un riferimento al rundown compatibile con la cache offre il vantaggio di migliorare le prestazioni e la scalabilità in situazioni specifiche, ma utilizza più memoria rispetto a un normale riferimento di rundown. È consigliabile considerare questo compromesso quando si sceglie tra i due tipi di riferimenti di rundown.