System Worker Threads

Um driver que requer processamento atrasado pode usar um item de trabalho, que contém um ponteiro para uma rotina de retorno de chamada de driver que executa o processamento real. O driver enfileira o item de trabalho e um thread de trabalho do sistema remove o item de trabalho da fila e executa a rotina de retorno de chamada do driver. O sistema mantém um pool desses threads de trabalho do sistema, que são threads do sistema que processam um item de trabalho por vez.

O driver associa uma rotina de retorno de chamada do WorkItem ao item de trabalho. Quando o thread de trabalho do sistema processa o item de trabalho, ele chama a rotina WorkItem associada. No Windows Vista e versões posteriores do Windows, um driver pode associar uma rotina WorkItemEx a um item de trabalho. WorkItemEx usa parâmetros diferentes dos parâmetros que o WorkItem usa.

As rotinas WorkItem e WorkItemEx são executadas em um contexto de thread do sistema. Se uma rotina de expedição de driver puder ser executada em um contexto de thread no modo de usuário, essa rotina poderá chamar uma rotina WorkItem ou WorkItemEx para executar quaisquer operações que exijam um contexto de thread do sistema.

Para usar um item de trabalho, um driver executa as seguintes etapas:

  1. Alocar e inicializar um novo item de trabalho.

    O sistema usa uma estrutura IO_WORKITEM para manter um item de trabalho. Para alocar uma nova estrutura de IO_WORKITEM e inicializá-la como um item de trabalho, o driver pode chamar IoAllocateWorkItem. No Windows Vista e versões posteriores do Windows, o driver pode, como alternativa, alocar sua própria estrutura IO_WORKITEM e chamar IoInitializeWorkItem para inicializar a estrutura como um item de trabalho. (O driver deve chamar IoSizeofWorkItem para determinar o número de bytes necessários para manter um item de trabalho.)

  2. Associe uma rotina de retorno de chamada ao item de trabalho e enfileira o item de trabalho para que ele seja processado por um thread de trabalho do sistema.

    Para associar uma rotina WorkItem ao item de trabalho e enfileirar o item de trabalho, o driver deve chamar IoQueueWorkItem. Para associar uma rotina WorkItemEx ao item de trabalho e enfileirar o item de trabalho, o driver deve chamar IoQueueWorkItemEx.

  3. Depois que o item de trabalho não for mais necessário, libere-o.

    Um item de trabalho alocado por IoAllocateWorkItem deve ser liberado por IoFreeWorkItem. Um item de trabalho inicializado por IoInitializeWorkItem deve ser não inicializado por IoUninitializeWorkItem antes de ser liberado.

    O item de trabalho só pode ser não inicializado ou liberado quando o item de trabalho não está na fila no momento. O sistema desativa o item de trabalho antes de chamar a rotina de retorno de chamada do item de trabalho, para que IoFreeWorkItem e IoUninitializeWorkItem possam ser chamados de dentro do retorno de chamada.

Um DPC que precisa iniciar uma tarefa de processamento que exija processamento longo ou que faça uma chamada de bloqueio deve delegar o processamento dessa tarefa a um ou mais itens de trabalho. Enquanto um DPC é executado, todos os threads são impedidos de serem executados. Além disso, um DPC, que é executado em IRQL = DISPATCH_LEVEL, não deve fazer chamadas de bloqueio. No entanto, o thread de trabalho do sistema que processa um item de trabalho é executado em IRQL = PASSIVE_LEVEL. Assim, o item de trabalho pode conter chamadas de bloqueio. Por exemplo, um thread de trabalho do sistema pode aguardar em um objeto dispatcher.

Como o pool de threads de trabalho do sistema é um recurso limitado, as rotinas WorkItem e WorkItemEx só podem ser usadas para operações que levam um curto período de tempo. Se uma dessas rotinas for executada por muito tempo (se contiver um loop indefinido, por exemplo) ou aguardar muito tempo, o sistema poderá fazer deadlock. Portanto, se um driver exigir longos períodos de processamento atrasado, ele deverá chamar PsCreateSystemThread para criar seu próprio thread do sistema.

Não chame IoQueueWorkItem ou IoQueueWorkItemEx para enfileirar um item de trabalho que já esteja na fila. Isso pode causar corrupção de estruturas de dados do sistema. Se o driver enfileirar o mesmo item de trabalho sempre que uma rotina de driver específica for executada, você poderá usar a seguinte técnica para evitar enfileirar o item de trabalho uma segunda vez se ele já estiver na fila:

  • O driver mantém uma lista de tarefas para a rotina de trabalho.
  • Essa lista de tarefas está disponível no contexto fornecido à rotina de trabalho. A rotina de trabalho e todas as rotinas de driver que modificam a lista de tarefas sincronizam seu acesso à lista.
  • Sempre que a rotina de trabalho é executada, ela executa todas as tarefas na lista e remove cada tarefa da lista à medida que a tarefa é concluída.
  • Quando uma nova tarefa chega, o driver adiciona essa tarefa à lista. O driver enfileira o item de trabalho somente se a lista de tarefas estava vazia anteriormente.

O thread de trabalho do sistema remove o item de trabalho da fila antes de chamar o thread de trabalho. Assim, um thread de driver pode enfileirar com segurança o item de trabalho novamente assim que o thread de trabalho começar a ser executado.