Thread di lavoro di sistema

Un driver che richiede un'elaborazione ritardata può usare un elemento di lavoro che contiene un puntatore a una routine di callback del driver che esegue l'elaborazione effettiva. Il driver accoda l'elemento di lavoro e un thread di lavoro di sistema rimuove l'elemento di lavoro dalla coda ed esegue la routine di callback del driver. Il sistema gestisce un pool di questi thread di lavoro di sistema, ovvero thread di sistema che ogni processo un elemento di lavoro alla volta.

Il driver associa una routine di callback WorkItem all'elemento di lavoro. Quando il thread di lavoro di sistema elabora l'elemento di lavoro, chiama la routine WorkItem associata. In Windows Vista e versioni successive di Windows, un driver può invece associare una routine WorkItemEx a un elemento di lavoro. WorkItemEx accetta parametri diversi dai parametri accettati da WorkItem .

Le routine WorkItem e WorkItemEx vengono eseguite in un contesto di thread di sistema. Se una routine dispatch driver può essere eseguita in un contesto di thread in modalità utente, tale routine può chiamare una routine WorkItem o WorkItemEx per eseguire qualsiasi operazione che richieda un contesto di thread di sistema.

Per usare un elemento di lavoro, un driver esegue la procedura seguente:

  1. Allocare e inizializzare un nuovo elemento di lavoro.

    Il sistema usa una struttura IO_WORKITEM per contenere un elemento di lavoro. Per allocare una nuova struttura IO_WORKITEM e inizializzarla come elemento di lavoro, il driver può chiamare IoAllocateWorkItem. In Windows Vista e versioni successive di Windows, il driver può in alternativa allocare la propria struttura IO_WORKITEM e chiamare IoInitializeWorkItem per inizializzare la struttura come elemento di lavoro. Il driver deve chiamare IoSizeofWorkItem per determinare il numero di byte necessari per contenere un elemento di lavoro.

  2. Associare una routine di callback all'elemento di lavoro e accodare l'elemento di lavoro in modo che venga elaborato da un thread di lavoro di sistema.

    Per associare una routine WorkItem all'elemento di lavoro e accodare l'elemento di lavoro, il driver deve chiamare IoQueueWorkItem. Per associare invece una routine WorkItemEx all'elemento di lavoro e accodare l'elemento di lavoro, il driver deve chiamare IoQueueWorkItemEx.

  3. Dopo che l'elemento di lavoro non è più necessario, liberarlo.

    Un elemento di lavoro allocato da IoAllocateWorkItem deve essere liberato da IoFreeWorkItem. Un elemento di lavoro inizializzato da IoInitializeWorkItem deve essere inizializzato da IoUninitializeWorkItem prima che possa essere liberato.

    L'elemento di lavoro può essere non inizializzato o liberato solo quando l'elemento di lavoro non è attualmente in coda. Il sistema annulla la coda dell'elemento di lavoro prima di chiamare la routine di callback dell'elemento di lavoro, quindi IoFreeWorkItem e IoUninitializeWorkItem possono essere chiamati dall'interno del callback.

Un DPC che deve avviare un'attività di elaborazione che richiede un'elaborazione lunga o che effettua una chiamata di blocco deve delegare l'elaborazione di tale attività a uno o più elementi di lavoro. Durante l'esecuzione di un DPC, non è possibile eseguire tutti i thread. Inoltre, un DPC, che viene eseguito in IRQL = DISPATCH_LEVEL, non deve eseguire chiamate di blocco. Tuttavia, il thread di lavoro di sistema che elabora un elemento di lavoro viene eseguito in IRQL = PASSIVE_LEVEL. Pertanto, l'elemento di lavoro può contenere chiamate di blocco. Ad esempio, un thread di lavoro di sistema può attendere su un oggetto dispatcher.

Poiché il pool di thread di lavoro di sistema è una risorsa limitata, le routine WorkItem e WorkItemEx possono essere usate solo per le operazioni che richiedono un breve periodo di tempo. Se una di queste routine viene eseguita per troppo tempo (se contiene un ciclo indefinito, ad esempio) o attende troppo tempo, il sistema può bloccarsi. Pertanto, se un driver richiede lunghi periodi di elaborazione ritardata, deve invece chiamare PsCreateSystemThread per creare il proprio thread di sistema.

Non chiamare IoQueueWorkItem o IoQueueWorkItemEx per accodare un elemento di lavoro già presente nella coda. Questa operazione può causare il danneggiamento delle strutture dei dati di sistema. Se il driver accoda lo stesso elemento di lavoro ogni volta che viene eseguita una routine del driver specifica, è possibile usare la tecnica seguente per evitare di accodare l'elemento di lavoro una seconda volta se si trova già nella coda:

  • Il driver gestisce un elenco di attività per la routine di lavoro.
  • Questo elenco di attività è disponibile nel contesto fornito alla routine di lavoro. La routine di lavoro e tutte le routine driver che modificano l'elenco attività sincronizzano l'accesso all'elenco.
  • Ogni volta che viene eseguita la routine di lavoro, esegue tutte le attività nell'elenco e rimuove ogni attività dall'elenco al termine dell'attività.
  • Quando arriva una nuova attività, il driver aggiunge questa attività all'elenco. Il driver accoda l'elemento di lavoro solo se l'elenco attività era precedentemente vuoto.

Il thread di lavoro di sistema rimuove l'elemento di lavoro dalla coda prima di chiamare il thread di lavoro. Pertanto, un thread driver può accodare l'elemento di lavoro in modo sicuro non appena viene avviato l'esecuzione del thread di lavoro.