Condividi tramite


Uso della sincronizzazione automatica

Quasi tutto il codice in un driver basato su framework risiede nelle funzioni di callback degli eventi. Il framework sincronizza automaticamente la maggior parte delle funzioni di callback di un driver, come indicato di seguito:

  • Il framework sincronizza sempre le funzioni di callback degli eventi per oggetto dispositivo generale, oggetto dispositivo funzionale (FDO)e oggetto dispositivo fisico (PDO) tra loro. Solo una delle funzioni di callback può essere chiamata alla volta per ogni dispositivo. L'eccezione è EvtDeviceSurpriseRemoval, EvtDeviceQueryRemovee EvtDeviceQueryStop. Queste funzioni di callback supportano gli eventi Plug and Play (PnP) e gestione dell'energia e vengono chiamate in IRQL = PASSIVE_LEVEL.

  • Facoltativamente, il framework può sincronizzare l'esecuzione delle funzioni di callback che gestiscono le richieste di I/O di un driver, in modo che queste funzioni di callback eseguano una alla volta. In particolare, il framework può sincronizzare le funzioni di callback per la coda , l'interrupt , la chiamata di routine posticipata (DPC) , il timer , l'elemento di lavoro e gli oggetti file , insieme alla funzione di callback dell'oggetto richiesta EvtRequestCancel. Il framework chiama la maggior parte di queste funzioni di richiamata in IRQL = DISPATCH_LEVEL, ma è possibile forzare l'esecuzione delle funzioni di richiamata delle code e degli oggetti di file in IRQL = PASSIVE_LEVEL. Le funzioni di callback degli elementi di lavoro vengono sempre eseguite in PASSIVE_LEVEL.

Il framework implementa questa sincronizzazione automatica usando un set di blocchi di sincronizzazione interni. Il framework garantisce che due o più thread non possano chiamare contemporaneamente la stessa funzione di callback. Ogni thread deve attendere fino a quando non può acquisire un blocco di sincronizzazione prima di chiamare una funzione di callback. Facoltativamente, i driver possono acquisire questi blocchi di sincronizzazione quando necessario. Per ulteriori informazioni, vedere Utilizzo dei Blocchi del Framework.

Il driver deve archiviare dati specifici dell'oggetto nello spazio del contesto dell'oggetto . Se il driver usa solo interfacce definite dal framework, solo le funzioni di callback che ricevono un handle per l'oggetto possono accedere a questi dati. Se il framework sincronizza le chiamate alle funzioni di callback del driver, viene chiamata una sola funzione di callback alla volta. Lo spazio di contesto dell'oggetto è accessibile a una sola funzione di callback alla volta.

A meno che il driver non implementi la gestione degli interrupt a livello passivo, il codice che gestisce gli interrupt e accede ai dati di interrupt deve essere eseguito all'IRQL (DIRQL) del dispositivo e richiede una sincronizzazione aggiuntiva. Per altre informazioni, vedere Sincronizzazione del codice interrupt.

Se il driver abilita la sincronizzazione automatica delle funzioni di callback che gestiscono le richieste di I/O, il framework sincronizza queste funzioni di callback in modo che eseguano una alla volta. Nella tabella seguente sono elencate le funzioni di callback sincronizzate dal framework.

Tipo di oggetto Funzioni di callback sincronizzate

Oggetto Queue

gestori di richieste, EvtIoQueueState, EvtIoResume, EvtIoStop

File oggetto

Tutte le funzioni di callback

Oggetto della richiesta

EvtRequestCancel

Facoltativamente, il framework può anche sincronizzare queste funzioni di callback con qualsiasi delle funzioni di callback degli oggetti interrupt, DPC, elemento di lavoro e timer fornite dal driver per il dispositivo (escludendo la funzione di callback dell'oggetto interrupt EvtInterruptIsr). Per abilitare questa sincronizzazione aggiuntiva, il driver deve impostare il membro AutomaticSerialization delle strutture di configurazione di questi oggetti su TRUE.

In sintesi, la funzionalità di sincronizzazione automatica del framework offre le funzionalità seguenti:

  • Il framework sincronizza sempre le funzioni di callback PnP e risparmio energia di ogni dispositivo.

  • Facoltativamente, il framework può sincronizzare i gestori delle richieste di una coda di I/O e alcune funzioni di callback aggiuntive (vedere la tabella precedente).

  • Un driver può chiedere al framework di sincronizzare le funzioni di callback per gli oggetti interrupt, DPC, elemento di lavoro e timer.

  • I driver devono sincronizzare il codice che gestisce gli interrupt e accede ai dati di interrupt utilizzando le tecniche descritte in La sincronizzazione del codice interrupt.

  • Il framework non sincronizza le altre funzioni di callback di un driver, come la funzione di callback CompletionRoutine del driver o le funzioni di callback definite dall'oggetto di destinazione di I/O. Il framework, invece, fornisce ulteriori lucchetti che i driver possono usare per sincronizzare le funzioni di callback.

Scelta di un ambito di sincronizzazione

Puoi scegliere di far sì che il framework sincronizzi tutte le funzioni di callback associate a tutte le code di I/O di un dispositivo. In alternativa, è possibile scegliere di fare in modo che il framework sincronizzi separatamente le funzioni di callback per ognuna delle code di I/O di un dispositivo. Le opzioni di sincronizzazione disponibili per il driver sono le seguenti:

  • Sincronizzazione a livello di dispositivo

    Il framework sincronizza le funzioni di callback per tutte le code di I/O del dispositivo, in modo che vengano eseguite una alla volta. Il framework ottiene questa sincronizzazione acquisendo il blocco di sincronizzazione del dispositivo prima di chiamare una funzione di callback.

  • Sincronizzazione a livello di coda

    Il framework sincronizza le funzioni di callback per ogni singola coda di I/O, in modo che eseguano una alla volta. Il framework ottiene questa sincronizzazione acquisendo il blocco di sincronizzazione della coda prima di chiamare una funzione di callback.

  • Nessuna sincronizzazione

    Il framework non sincronizza l'esecuzione delle funzioni di callback nella tabella precedente e non acquisisce un blocco di sincronizzazione prima di chiamare le funzioni di callback. Se è necessaria la sincronizzazione, il driver deve specificarlo.

Per specificare se si vuole che il framework fornisca la sincronizzazione a livello di dispositivo, la sincronizzazione a livello di coda o nessuna sincronizzazione per il driver, specificare un ambito di sincronizzazione per l'oggetto driver, gli oggetti dispositivo o gli oggetti coda. Il SynchronizationScope della struttura di WDF_OBJECT_ATTRIBUTES di un oggetto identifica l'ambito di sincronizzazione dell'oggetto. I valori dell'ambito di sincronizzazione che il driver può specificare sono:

WdfSynchronizationScopeDevice
Il framework si sincronizza ottenendo il blocco di sincronizzazione di un oggetto dispositivo.

WdfSynchronizationScopeQueue
Il framework si sincronizza ottenendo il blocco di sincronizzazione di un oggetto coda.

WdfSynchronizationScopeNone
Il framework non sincronizza e non ottiene un blocco di sincronizzazione.

WdfSynchronizationScopeInheritFromParent
Il framework ottiene il valore SynchronizationScope dell'oggetto dall'oggetto padre.

In generale, evitare di usare la sincronizzazione a livello di dispositivo.

Per altre informazioni sui valori dell'ambito di sincronizzazione, vedere WDF_SYNCHRONIZATION_SCOPE.

L'ambito di sincronizzazione predefinito per gli oggetti driver è WdfSynchronizationScopeNone. L'ambito di sincronizzazione predefinito per gli oggetti dispositivo e coda è WdfSynchronizationScopeInheritFromParent.

Per usare il framework per fornire la sincronizzazione a livello di dispositivo per tutti i dispositivi, impostare SynchronizationScope su WdfSynchronizationScopeDevice nella struttura WDF_OBJECT_ATTRIBUTES dell'oggetto driver driver del driver. Usare il valore predefinito WdfSynchronizationScopeInheritFromParent per ogni oggetto dispositivo .

Per fornire la sincronizzazione a livello di dispositivo per singoli dispositivi, usare il valore predefinito WdfSynchronizationScopeNone per l'oggetto driver. Impostare SynchronizationScope su WdfSynchronizationScopeDevice nella struttura WDF_OBJECT_ATTRIBUTES di singoli oggetti dispositivo.

Se si vuole che il framework fornisca la sincronizzazione a livello di coda per un dispositivo, è possibile usare le tecniche seguenti:

  • Per le versioni framework 1.9 e successive, abilitare la sincronizzazione a livello di coda per le singole code impostando WdfSynchronizationScopeQueue nella struttura WDF_OBJECT_ATTRIBUTES dell'oggetto coda. Questa tecnica è preferibile.

  • In alternativa, è possibile usare i passaggi seguenti in tutte le versioni del framework:

    1. Impostare SynchronizationScope su WdfSynchronizationScopeQueue nella struttura WDF_OBJECT_ATTRIBUTES dell'oggetto del dispositivo.
    2. Usare il valore predefinito WdfSynchronizationScopeInheritFromParent per gli oggetti di coda di ogni dispositivo.

Se non si vuole che il framework sincronizzi le funzioni di callback che gestiscono le richieste di I/O del driver, usare il valore predefinito SynchronizationScope per gli oggetti driver, dispositivo e coda del driver. In questo caso, il framework non sincronizza automaticamente le funzioni di callback correlate alle richieste di I/O del driver. Il framework può chiamare le funzioni di callback in IRQL <= DISPATCH_LEVEL.

L'impostazione di un valore SynchronizationScope sincronizza solo le funzioni di callback contenute nella tabella precedente. Se si desidera che il framework sincronizzi anche le funzioni di callback di interrupt, DPC, work item e oggetto timer del driver, impostare il membro AutomaticSerialization delle strutture di configurazione di questi oggetti su TRUE.

Tuttavia, è possibile impostare AutomaticSerialization su TRUE solo se tutte le funzioni di callback da sincronizzare vengono eseguite nello stesso IRQL. La scelta di un livello di esecuzione , descritto di seguito, potrebbe comportare livelli IRQL incompatibili. In una situazione di questo tipo, il driver deve usare i blocchi del framework anziché impostare AutomaticSerialization. Per altre informazioni sulle strutture di configurazione per gli oggetti interrupt, DPC, elementi di lavoro e timer e per altre informazioni sulle restrizioni applicabili all'impostazione di AutomaticSerialization in queste strutture, vedere WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIGe WDF_TIMER_CONFIG.

Impostare AutomaticSerialization su TRUE, selezionare la sincronizzazione a livello di coda.

Scelta di un livello di esecuzione

Quando un driver crea alcuni tipi di oggetti framework, può specificare un livello di esecuzione per l'oggetto. Il livello di esecuzione specifica l'IRQL in corrispondenza del quale il framework chiama le funzioni di callback dell'evento dell'oggetto che gestiscono le richieste di I/O di un driver.

Se un driver fornisce un livello di esecuzione, il livello fornito influisce sulle funzioni di callback per gli oggetti coda e file. In genere, se il driver usa la sincronizzazione automatica, il framework chiama queste funzioni di callback in IRQL = DISPATCH_LEVEL. Specificando un livello di esecuzione, il driver può forzare il framework a chiamare queste funzioni di callback in IRQL = PASSIVE_LEVEL. Il framework utilizza le seguenti regole quando imposta l'IRQL al quale chiama le funzioni di callback della coda e dell'oggetto file:

  • Se un driver utilizza la sincronizzazione automatica, il framework chiama le funzioni di callback della coda e dell'oggetto file a IRQL = DISPATCH_LEVEL, a meno che il driver non richieda al framework di chiamare le sue funzioni di callback a IRQL = PASSIVE_LEVEL.

  • Se un driver non utilizza la sincronizzazione automatica e non specifica un livello di esecuzione, il framework può chiamare le funzioni di callback del driver relative alla coda e all'oggetto file in IRQL <= DISPATCH_LEVEL.

Se il driver fornisce funzioni di callback dell'oggetto file, probabilmente desideri che il framework chiami queste funzioni di callback a IRQL = PASSIVE_LEVEL perché alcuni dati di file, ad esempio il nome del file, sono paginabili.

Per fornire un livello di esecuzione, il driver deve specificare un valore per il membro ExecutionLevel della struttura di WDF_OBJECT_ATTRIBUTES di un oggetto. I valori del livello di esecuzione che il driver può specificare sono:

WdfExecutionLevelPassive
Il framework chiama le funzioni di callback dell'oggetto quando il livello IRQL è PASSIVE_LEVEL.

WdfExecutionLevelDispatch
Il framework può chiamare, in IRQL <= DISPATCH_LEVEL, le funzioni di callback dell'oggetto. Se il driver usa la sincronizzazione automatica, il framework chiama sempre le funzioni di callback in IRQL = DISPATCH_LEVEL.

WdfExecutionLevelInheritFromParent
Il framework ottiene il valore ExecutionLevel dell'oggetto dal suo oggetto padre.

Il livello di esecuzione predefinito per gli oggetti driver è WdfExecutionLevelDispatch. Il livello di esecuzione predefinito per tutti gli altri oggetti è WdfExecutionLevelInheritFromParent.

Per altre informazioni sui valori del livello di esecuzione, vedere WDF_EXECUTION_LEVEL.

La tabella seguente illustra il livello IRQL in cui il framework può chiamare le funzioni di callback di un driver per oggetti coda e oggetti file.

Ambito di sincronizzazione Livello di esecuzione IRQL delle funzioni di callback per le code e i file

WdfSynchronizationScopeDevice

WdfExecutionLevelPassive

LIVELLO PASSIVO

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

LIVELLO_DI_GESTIONE

WdfSynchronizationScopeQueue

WdfExecutionLevelPassive

LIVELLO PASSIVO

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

LIVELLO_DI_GESTIONE

WdfSynchronizationScopeNone

WdfExecutionLevelPassive

LIVELLO PASSIVO

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= LIVELLO_DI_INVIO

È possibile impostare il livello di esecuzione tra WdfExecutionLevelPassive e WdfExecutionLevelDispatch per gli oggetti driver, dispositivo, file, coda, timer e generali. Per altri oggetti, è possibile impostare solo WdfExecutionLevelInheritFromParent.

Specificare WdfExecutionLevelPassive se:

  • Le funzioni di callback del driver devono chiamare metodi framework o routine WDM (Windows Driver Model) che è possibile chiamare solo in IRQL = PASSIVE_LEVEL.

  • Le funzioni di callback del driver devono accedere a codice o dati paginabili. Ad esempio, le funzioni di callback dell'oggetto file accedono solitamente a dati paginabili.

Anziché impostare WdfExecutionLevelPassive, il driver può impostare WdfExecutionLevelDispatch e fornire una funzione di callback che crea elementi di lavoro se deve gestire alcune operazioni a IRQL = PASSIVE_LEVEL.

Prima di decidere se il driver deve impostare il livello di esecuzione di un oggetto su WdfExecutionLevelPassive, determinare l'IRQL in cui vengono chiamati il driver e altri driver nello stack di driver. Si considerino le situazioni seguenti:

  • Se il driver si trova alla sommità dello stack dei driver in modalità kernel, il sistema chiama in genere il driver a IRQL = PASSIVE_LEVEL. Il client di tale driver potrebbe essere un driver basato su UMDF o un'applicazione in modalità utente. Se si specifica WdfExecutionLevelPassive non influisce negativamente sulle prestazioni del driver, perché il framework non deve accodare le chiamate del driver agli elementi di lavoro chiamati in IRQL = PASSIVE_LEVEL.

  • Se il driver non è in cima allo stack, è probabile che il sistema non chiami il driver a IRQL = PASSIVE_LEVEL. Pertanto, il framework deve accodare le chiamate del driver agli elementi di lavoro, che vengono chiamati successivamente con IRQL = PASSIVE_LEVEL. Questo processo può causare prestazioni scarse del driver, rispetto a consentire che le funzioni di callback del driver vengano chiamate a IRQL <= DISPATCH_LEVEL.

Per gli oggetti DPC e per gli oggetti timer che non rappresentano timer a livello passivo, non è possibile configurare il membro della struttura di configurazione su TRUE se si imposta il livello di esecuzione del dispositivo padre su WdfExecutionLevelPassive. Il framework acquisisce i blocchi di sincronizzazione del callback dell'oggetto dispositivo a IRQL = PASSIVE_LEVEL. Pertanto, non può usare i blocchi per sincronizzare le funzioni di callback degli oggetti DPC o timer, che devono essere eseguite in IRQL = DISPATCH_LEVEL. In questo caso, il driver deve usare blocchi di selezione del framework in qualsiasi dispositivo, DPC o funzioni di callback di oggetti timer che devono essere sincronizzate tra loro.

Si noti anche che per gli oggetti timer che rappresentano timer a livello passivo, è possibile impostare il membro AutomaticSerialization della struttura di configurazione su TRUE solo se il livello di esecuzione del dispositivo padre è impostato su WdfExecutionLevelPassive.