Gestione delle code interlock con un thread Driver-Created
I nuovi driver devono usare il framework di accodamento IRP indipendente dall'annullamento in preferenza per i metodi descritti in questa sezione.
Come il driver del controller floppy di sistema, un driver con un thread dedicato al dispositivo, anziché una routine StartIo , gestisce in genere la propria accodamento di IRP in una coda interlock collegata doubly. Il thread del driver esegue il pull dei runtime di integrazione dalla coda interlock quando è necessario eseguire operazioni sul dispositivo.
In generale, il driver deve gestire la sincronizzazione con il thread con qualsiasi risorsa condivisa tra il thread e altre routine del driver. Il driver deve anche avere un modo per notificare al thread creato dal driver che i runtime di integrazione sono in coda. In genere, il thread attende un oggetto dispatcher, archiviato nell'estensione del dispositivo, fino a quando le routine Dispatch del driver impostano l'oggetto dispatcher sullo stato Signaled dopo l'inserimento di un IRP nella coda interlocked.
Quando vengono chiamate le routine Dispatch del driver, ognuna controlla i parametri nella posizione dello stack di I/O dell'IRP di input e, se sono validi, accoda la richiesta per un'ulteriore elaborazione. Per ogni IRP accodato a un thread dedicato al driver, la routine dispatch deve configurare il contesto necessario per elaborare tale IRP prima di chiamare ExInterlockedInsertXxxList. La posizione dello stack di I/O del driver in ogni IRP consente al thread del driver di accedere all'estensione del dispositivo di destinazione dell'oggetto dispositivo di destinazione, in cui il driver può condividere le informazioni di contesto con il relativo thread, perché il thread rimuove ogni IRP dalla coda.
Un driver che accoda irP annullabili deve implementare una routine Cancel . Poiché i runtime di integrazione vengono annullati in modo asincrono, è necessario assicurarsi che il driver eviti le race condition che possono verificarsi. Vedere Synchronizing IRP Cancellation (Sincronizzazione dell'annullamento di IRP ) Per altre informazioni sulle race condition associate all'annullamento di IRP e tecniche per evitarle.
Qualsiasi thread creato dal driver viene eseguito in IRQL = PASSIVE_LEVEL e in una priorità di runtime di base impostata in precedenza quando il driver denominato PsCreateSystemThread. La chiamata del thread a ExInterlockedRemoveHeadList genera temporaneamente irQL per DISPATCH_LEVEL sul processore corrente mentre l'IRP viene rimosso dalla coda interna del driver. Il runtime di integrazione originale viene ripristinato in modo da PASSIVE_LEVEL al ritorno da questa chiamata.
Qualsiasi thread di driver (o callback del thread di lavoro fornito dal driver) deve gestire attentamente i runtime di integrazione in cui viene eseguito. come illustrato nell'esempio seguente:
Poiché i thread di sistema in genere vengono eseguiti in IRQL = PASSIVE_LEVEL, è possibile che un thread del driver attenda che gli oggetti dispatcher definiti dal kernel vengano impostati sullo stato segnalato.
Ad esempio, un thread dedicato al dispositivo potrebbe attendere che altri driver soddisfino un evento e completino un certo numero di IRP di trasferimento parziale che il thread configura con IoBuildSynchronousFsdRequest.
Tuttavia, tale thread dedicato al dispositivo deve generare IRQL nel processore corrente prima di chiamare determinate routine di supporto.
Ad esempio, se un driver usa DMA, il thread dedicato al dispositivo deve annidare le chiamate a AllocateAdapterChannel e FreeAdapterChannel tra le chiamate a KeRaiseIrql e KeLowerIrql perché queste routine e alcune altre routine di supporto per le operazioni DMA devono essere chiamate in IRQL = DISPATCH_LEVEL.
Tenere presente che le routine StartIo vengono eseguite in DISPATCH_LEVEL, quindi i driver che usano DMA non devono effettuare chiamate alle routine KeXxxIrql dalle routine StartIo .
Un thread creato dal driver può accedere alla memoria visualizzabile perché viene eseguito in un contesto di thread non arbiverso (proprio) in IRQL = PASSIVE_LEVEL, ma molte altre routine del driver standard vengono eseguite in IRQL >= DISPATCH_LEVEL. Se un thread creato dal driver alloca memoria accessibile da una routine di questo tipo, deve allocare la memoria da un pool non di paging. Ad esempio, se un thread dedicato al dispositivo alloca qualsiasi buffer a cui verrà eseguito l'accesso in un secondo momento da ISR o SynchCritSection, AdapterControl, AdapterListControl, ControllerControl, DpcForIsr, CustomDpc, IoTimer, CustomTimerDpc o, in un driver di livello superiore, la routine IoCompletion , la memoria allocata dal thread non può essere impaginabile.
Se il driver gestisce le informazioni sullo stato condivise o le risorse in un'estensione del dispositivo, un thread del driver (come una routine StartIo ) deve sincronizzare l'accesso a un dispositivo fisico e ai dati condivisi con le altre routine del driver che accedono allo stesso dispositivo, posizione di memoria o risorse.
Se il thread condivide il dispositivo o lo stato con l'ISR, deve usare KeSynchronizeExecution per chiamare una routine SynchCritSection fornita dal driver per programmare il dispositivo o per accedere allo stato condiviso. Vedere Uso delle sezioni critiche.
Se il thread condivide lo stato o le risorse con routine diverse dall'ISR, il driver deve proteggere lo stato condiviso o le risorse con un blocco spin esecutivo inizializzato dal driver per il quale il driver fornisce l'archiviazione. Per altre informazioni, vedere Blocchi di selezione.
Per altre informazioni sui compromessi di progettazione di un oggetto usando un thread driver per un dispositivo lento, vedere Polling a Device (Polling a Device). Vedere anche Gestione delle priorità hardware. Per informazioni specifiche sui runtime di integrazione per determinate routine di supporto, vedere la pagina di riferimento della routine.