Cancel-Safe code IRP
I driver che implementano le proprie accodamento IRP devono usare il framework di coda IRP non sicuro per l'annullamento . Le code IRP annullate dividono la gestione di IRP in due parti:
Il driver fornisce un set di routine di callback che implementano operazioni standard nella coda IRP del driver. Le operazioni fornite includono l'inserimento e la rimozione di IRP dalla coda e il blocco e lo sblocco della coda. Vedere Implementazione della coda IRP Cancel-Safe.
Ogni volta che il driver deve effettivamente inserire o rimuovere un'IRP dalla coda, usa le routine IoCsqXxx fornite dal sistema. Queste routine gestiscono tutta la sincronizzazione e la logica di annullamento di IRP per il driver.
I driver che usano code IRP non implementano routine Cancel per supportare l'annullamento di IRP.
Il framework garantisce che i driver inseriscono e rimuoveno gli IRP dalla coda atomicamente. Garantisce inoltre che l'annullamento di IRP venga implementato correttamente. I driver che non usano il framework devono bloccare manualmente e sbloccare la coda prima di eseguire eventuali inserimenti ed eliminazioni. Devono anche evitare le condizioni di gara che possono risultare quando si implementa una routine Annulla . Per una descrizione delle condizioni di gara che possono verificarsi, vedere Sincronizzazione dell'annullamento di IRP.
Il framework di coda IRP cancel-safe è incluso in Windows XP e versioni successive di Windows. I driver che devono funzionare anche con Windows 2000 e Windows 98/Me possono collegarsi alla libreria Csq.lib inclusa in Windows Driver Kit (WDK). La libreria Csq.lib fornisce un'implementazione di questo framework.
Le routine IoCsqXxx vengono dichiarate in Windows XP e versioni successive di Wdm.h e Ntddk.h. I driver che devono funzionare anche con Windows 2000 e Windows 98/Me devono includere Csq.h per le dichiarazioni.
È possibile visualizzare una dimostrazione completa di come usare le code IRP annullate nella directory \src\general\cancel della WDK. Per altre informazioni su queste code, vedere anche flusso di controllo per Cancel-Safe white paper di accodamento IRP .
Implementazione della coda IRP Cancel-Safe
Per implementare una coda IRP sicura annulla, i driver devono fornire le routine seguenti:
Una delle routine seguenti per inserire irP nella coda: CsqInsertIrp o CsqInsertIrpEx. CsqInsertIrpEx è una versione estesa di CsqInsertIrp; la coda viene implementata usando una o l'altra.
Routine CsqRemoveIrp che rimuove l'IRP specificato dalla coda.
Routine CsqPeekNextIrp che restituisce un puntatore all'IRP successivo seguendo l'IRP specificato nella coda. Questo è il luogo in cui il sistema passa il valore PeekContext che riceve da IoCsqRemoveNextIrp. Il driver può interpretare tale valore in qualsiasi modo.
Entrambe le routine seguenti consentono al sistema di bloccare e sbloccare la coda IRP: CsqAcquireLock e CsqReleaseLock.
Routine CsqCompleteCanceledIrp che completa un'IRP annullata.
I puntatori alle routine del driver vengono archiviati nella struttura IO_CSQ che descrive la coda. Il driver alloca lo spazio di archiviazione per la struttura IO_CSQ . La struttura IO_CSQ è garantita che rimanga una dimensione fissa, in modo che un driver possa incorporare in modo sicuro la struttura all'interno dell'estensione del dispositivo.
Il driver usa IoCsqInitialize o IoCsqInitializeEx per inizializzare la struttura. Usare IoCsqInitialize se la coda implementa CsqInsertIrp o IoCsqInitializeEx se la coda implementa CsqInsertIrpEx.
I driver richiedono solo la funzionalità essenziale in ogni routine di callback. Ad esempio, solo le routine CsqAcquireLock e CsqReleaseLock implementano la gestione dei blocchi. Il sistema chiama automaticamente queste routine per bloccare e sbloccare la coda in base alle esigenze.
È possibile implementare qualsiasi tipo di meccanismo di accodamento IRP nel driver, purché vengano fornite le routine di invio appropriate. Ad esempio, il driver potrebbe implementare la coda come elenco collegato o come coda di priorità.
CsqInsertIrpEx offre un'interfaccia più flessibile per la coda rispetto a CsqInsertIrp. Il driver può usare il valore restituito per indicare il risultato dell'operazione; se restituisce un codice di errore, l'inserimento non è riuscito. Una routine CsqInsertIrp non restituisce un valore, pertanto non esiste un modo semplice per indicare che un inserimento non è riuscito. Inoltre, CsqInsertIrpEx accetta un parametro InsertContext definito dal driver aggiuntivo che può essere usato per specificare informazioni specifiche del driver aggiuntive da usare dall'implementazione della coda.
I driver possono usare CsqInsertIrpEx per implementare una gestione IRP più sofisticata. Ad esempio, se non sono presenti irp in sospeso, la routine CsqInsertIrpEx può restituire un codice di errore e il driver può elaborare immediatamente l'IRP. Analogamente, se i provider di integrazione non possono più essere accodati, CsqInsertIrpEx può restituire un codice di errore per indicare tale fatto.
Il driver è isolato da tutta la gestione dell'annullamento di IRP. Il sistema fornisce una routine Annulla per i provider di servizi di integrazione nella coda. Questa routine chiama CsqRemoveIrp per rimuovere l'IRP dalla coda e CsqCompleteCanceledIrp per completare l'annullamento di IRP.
Il diagramma seguente illustra il flusso di controllo per l'annullamento di IRP.
Di seguito è riportato un'implementazione di base di CsqCompleteCanceledIrp .
VOID CsqCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) {
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
I driver possono usare una delle primitive di sincronizzazione del sistema operativo per implementare le routine CsqAcquireLock e CsqReleaseLock . Le primitive di sincronizzazione disponibili includono blocchi di spin e oggetti mutex.
Ecco un esempio di come un driver può implementare il blocco usando blocchi di rotazione.
/*
The driver has previously initialized the SpinLock variable with
KeInitializeSpinLock.
*/
VOID CsqAcquireLock(PIO_CSQ IoCsq, PKIRQL PIrql)
{
KeAcquireSpinLock(SpinLock, PIrql);
}
VOID CsqReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
{
KeReleaseSpinLock(SpinLock, Irql);
}
Il sistema passa un puntatore a una variabile IRQL a CsqAcquireLock e CsqReleaseLock. Se il driver usa un blocco spin per implementare il blocco per la coda, il driver può usare questa variabile per archiviare l'irQL corrente quando la coda è bloccata.
I driver non sono necessari per usare i blocchi di rotazione. Ad esempio, il driver potrebbe usare un mutex per bloccare la coda. Per una descrizione delle tecniche di sincronizzazione disponibili per i driver, vedere Tecniche di sincronizzazione.
Uso della coda IRP Cancel-Safe
I driver usano le routine di sistema seguenti durante l'accodamento e la dequeuing di IRP:
Uno dei seguenti elementi da inserire nella coda: IoCsqInsertIrp o IoCsqInsertIrpEx.
IoCsqRemoveNextIrp per rimuovere il successivo IRP nella coda. Il driver può facoltativamente specificare un valore chiave.
Il diagramma seguente illustra il flusso di controllo per IoCsqRemoveNextIrp.
- IoCsqRemoveIrp per rimuovere l'IRP specificato dalla coda.
Il diagramma seguente illustra il flusso di controllo per IoCsqRemoveIrp.
Queste routine, a sua volta, inviano alle routine fornite dal driver.
La routine IoCsqInsertIrpEx fornisce l'accesso alle funzionalità estese di una routine CsqInsertIrpEx . Restituisce il valore di stato restituito da CsqInsertIrpEx. Il chiamante può usare questo valore per determinare se l'IRP è stato accodato o meno correttamente. IoCsqInsertIrpEx consente anche al chiamante di specificare un valore per il parametro InsertContext di CsqInsertIrpEx.
Si noti che sia IoCsqInsertIrp che IoCsqInsertIrpEx può essere chiamato in qualsiasi coda annullata, indipendentemente dal fatto che la coda abbia una routine CsqInsertIrp o una routine CsqInsertIrpEx . IoCsqInsertIrp si comporta nello stesso caso. Se IoCsqInsertIrpEx viene passata una coda con una routine CsqInsertIrp , si comporta in modo identico a IoCsqInsertIrp.
Il diagramma seguente illustra il flusso di controllo per IoCsqInsertIrp.
Il diagramma seguente illustra il flusso di controllo per IoCsqInsertIrpEx.
Esistono diversi modi naturali per usare le routine IoCsqXxx per accodare e dequeue IRP. Ad esempio, un driver potrebbe semplicemente eseguire l'elaborazione di IRP in coda nell'ordine in cui vengono ricevuti. Il driver può accodamento di un'istanza di IRP come segue:
status = IoCsqInsertIrpEx(IoCsq, Irp, NULL, NULL);
Se il driver non è necessario distinguere tra i particolari IRP, potrebbe semplicemente dequeuerli nell'ordine in cui sono stati accodati, come indicato di seguito:
IoCsqRemoveNextIrp(IoCsq, NULL);
In alternativa, il driver potrebbe accodare e dequeue specifici IP. Le routine usano la struttura di IO_CSQ_IRP_CONTEXT opaca per identificare determinati IRP nella coda. Il driver accoda l'IRP come segue:
IO_CSQ_IRP_CONTEXT ParticularIrpInQueue;
IoCsqInsertIrp(IoCsq, Irp, &ParticularIrpInQueue);
Il driver può quindi dequeare lo stesso IRP usando il valore IO_CSQ_IRP_CONTEXT .
IoCsqRemoveIrp(IoCsq, Irp, &ParticularIrpInQueue);
Il driver potrebbe essere necessario anche per rimuovere i provider di servizi di integrazione dalla coda in base a un determinato criterio. Ad esempio, il driver potrebbe associare una priorità a ogni IRP, in modo che i irP con priorità superiore vengano dequeuati prima. Il driver potrebbe passare un valore PeekContext a IoCsqRemoveNextIrp, che il sistema passa al driver quando richiede l'IRP successivo nella coda.