Gestione delle code interbloccate con il thread Driver-Created
I nuovi driver devono usare il framework di coda IRP sicura per l'annullamento piuttosto che ai metodi illustrati in questa sezione.
Come il driver del controller floppy di sistema, un driver con un thread dedicato al dispositivo, anziché una routine StartIo, in genere gestisce il proprio accodamento di IRP in una coda doppiamente collegata e interbloccata. Il thread del driver preleva gli IRP dalla sua coda sincronizzata quando è necessario eseguire operazioni sul dispositivo.
In generale, il driver deve gestire la sincronizzazione del thread con qualsiasi risorsa condivisa tra il thread e le altre routine del driver. Il driver deve anche avere un modo per notificare al thread creato dal driver che gli IRP sono stati messi 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 segnalato dopo aver inserito un IRP nella coda interbloccata.
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 dal driver, la routine di dispatch deve configurare tutto 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 dell'oggetto dispositivo di destinazione, in cui il driver può condividere le informazioni di contesto con il suo thread, mentre il thread rimuove ogni IRP dalla coda.
Un driver che accoda IRP annullabili deve implementare una routine Cancel. Poiché gli IRP vengono annullati in modo asincrono, è necessario assicurarsi che il driver eviti le condizioni di competizione che possono risultare. Consultare Synchronizing IRP Cancellation per maggiori dettagli sulle condizioni di competizione associate all'annullamento degli IRP e sulle 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 chiama PsCreateSystemThread. La chiamata del thread a ExInterlockedRemoveHeadList eleva temporaneamente l'IRQL al DISPATCH_LEVEL sul processore corrente mentre l'IRP viene rimosso dalla coda interna del driver. Il livello IRQL originale viene ripristinato a PASSIVE_LEVEL al ritorno da questa chiamata.
Qualsiasi thread di driver (o callback del thread di lavoro fornito dal driver) deve gestire attentamente gli IRQL a cui è sottoposto. Si consideri, ad esempio, quanto segue:
Poiché i thread di sistema vengono in genere 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 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 a livello DISPATCH_LEVEL. Pertanto, i driver che usano DMA non devono effettuare chiamate alle routine KeXxxIrql dalle loro 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 dal pool non di paging. Ad esempio, se un thread dedicato al dispositivo alloca un qualsiasi buffer a cui verrà eseguito l'accesso in un secondo momento dall'ISR del driver, oppure da SynchCritSection, AdapterControl, AdapterListControl, ControllerControl, DpcForIsr, CustomDpc, IoTimer, CustomTimerDpco, in un driver di livello superiore, dalla routine IoCompletion, la memoria allocata dal thread non può essere paginabile.
Se il driver mantiene le informazioni sullo stato condiviso o le risorse in un'estensione del dispositivo, un thread driver (ad esempio 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, alla posizione della memoria o alle risorse.
Se il thread condivide il dispositivo o lo stato con ISR, deve usare KeSynchronizeExecution per chiamare un driver fornito SynchCritSection routine per programmare il dispositivo o per accedere allo stato condiviso. Vedi Utilizzo delle Sezioni Critiche.
Se il thread condivide lo stato o le risorse con routine diverse dall'ISR, il driver deve proteggere lo stato o le risorse condivisi con uno spin lock esecutivo inizializzato dal driver, per il quale il driver fornisce la memoria. Per altre informazioni, vedere Spin Locks.
Per ulteriori informazioni sulle considerazioni di progettazione nell'utilizzo di un thread di comando per un dispositivo lento, vedere Polling di un dispositivo. 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.