Gerenciando filas interligadas com um thread de Driver-Created
Os novos drivers devem usar a estrutura de fila IRP com segurança de cancelamento em preferência para os métodos descritos nesta seção.
Assim como o driver do controlador de disquete do sistema, um driver com um thread dedicado ao dispositivo, em vez de uma rotina StartIo , geralmente gerencia sua própria fila de IRPs em uma fila interligada duplamente vinculada. O thread do driver efetua pull de IRPs de sua fila intertravada quando há trabalho a ser feito no dispositivo.
Em geral, o driver deve gerenciar a sincronização com seu thread para todos os recursos compartilhados entre o thread e outras rotinas de driver. O driver também deve ter alguma maneira de notificar o thread criado pelo driver de que os IRPs estão na fila. Normalmente, o thread aguarda em um objeto dispatcher, armazenado na extensão do dispositivo, até que as rotinas de Expedição do driver definam o objeto dispatcher como o estado Sinalizado depois de inserir um IRP na fila intertravada.
Quando as rotinas de Expedição do driver são chamadas, cada uma verifica os parâmetros no local da pilha de E/S do IRP de entrada e, se forem válidas, enfileira a solicitação para processamento adicional. Para cada IRP enfileirado em um thread dedicado ao driver, a rotina de expedição deve configurar qualquer contexto que seu thread precise para processar esse IRP antes de chamar ExInterlockedInsertXxxList. O local da pilha de E/S do driver em cada IRP fornece ao thread do driver acesso à extensão do dispositivo do objeto de dispositivo de destino, em que o driver pode compartilhar informações de contexto com seu thread, pois o thread remove cada IRP da fila.
Um driver que enfileira os IRPs canceláveis deve implementar uma rotina cancelar . Como os IRPs são cancelados de forma assíncrona, você deve garantir que o piloto evite as condições de corrida que podem resultar. Consulte Sincronizando o cancelamento de IRP para obter mais informações sobre as condições de corrida associadas ao cancelamento de IRPs e técnicas para evitá-los.
Qualquer thread criado pelo driver é executado em IRQL = PASSIVE_LEVEL e em uma prioridade de tempo de execução base definida anteriormente quando o driver chamado PsCreateSystemThread. A chamada do thread para ExInterlockedRemoveHeadList gera temporariamente o IRQL para DISPATCH_LEVEL no processador atual enquanto o IRP está sendo removido da fila interna do driver. O IRQL original é restaurado para PASSIVE_LEVEL no retorno dessa chamada.
Qualquer thread de driver (ou retorno de chamada de thread de trabalho fornecido pelo driver) deve gerenciar cuidadosamente os IRQLs em que ele é executado. Considere o seguinte exemplo:
Como os threads do sistema geralmente são executados em IRQL = PASSIVE_LEVEL, é possível que um thread de driver aguarde até que objetos dispatcher definidos pelo kernel sejam definidos como o estado sinalizado.
Por exemplo, um thread dedicado ao dispositivo pode aguardar que outros drivers satisfaçam um evento e conclua algum número de IRPs de transferência parcial que o thread configura com IoBuildSynchronousFsdRequest.
No entanto, esse thread dedicado ao dispositivo deve gerar IRQL no processador atual antes de chamar determinadas rotinas de suporte.
Por exemplo, se um driver usa DMA, seu thread dedicado ao dispositivo deve aninhar suas chamadas para AllocateAdapterChannel e FreeAdapterChannel entre chamadas para KeRaiseIrql e KeLowerIrql porque essas rotinas e determinadas outras rotinas de suporte para operações de DMA devem ser chamadas em IRQL = DISPATCH_LEVEL.
Lembre-se de que as rotinas do StartIo são executadas em DISPATCH_LEVEL, portanto, os drivers que usam DMA não precisam fazer chamadas para as rotinas KeXxxIrql de suas rotinas de StartIo .
Um thread criado pelo driver pode acessar a memória paginável porque é executado em um contexto de thread nonarbitrary (seu próprio) em IRQL = PASSIVE_LEVEL, mas muitas outras rotinas de driver padrão são executadas em IRQL >= DISPATCH_LEVEL. Se um thread criado pelo driver alocar memória que pode ser acessada por essa rotina, ele deverá alocar a memória do pool nãopagado. Por exemplo, se um thread dedicado ao dispositivo alocar qualquer buffer que será acessado posteriormente pelo ISR do driver ou SynchCritSection, AdapterControl, AdapterListControl, ControllerControl, DpcForIsr, CustomDpc, IoTimer, CustomTimerDpc ou, em um driver de nível superior, rotina IoCompletion , a memória alocada por thread não pode ser paginável.
Se o driver mantiver informações de estado compartilhado ou recursos em uma extensão de dispositivo, um thread de driver (como uma rotina StartIo ) deverá sincronizar seu acesso a um dispositivo físico e aos dados compartilhados com outras rotinas do driver que acessam o mesmo dispositivo, local de memória ou recursos.
Se o thread compartilhar o dispositivo ou o estado com o ISR, ele deverá usar KeSynchronizeExecution para chamar uma rotina SynchCritSection fornecida pelo driver para programar o dispositivo ou acessar o estado compartilhado. Consulte Usando seções críticas.
Se o thread compartilhar estado ou recursos com rotinas diferentes do ISR, o driver deverá proteger o estado compartilhado ou os recursos com um bloqueio de rotação executivo inicializado pelo driver para o qual o driver fornece o armazenamento. Para obter mais informações, consulte Spin Locks.
Para obter mais informações sobre as compensações de design de um usando um thread de driver para um dispositivo lento, consulte Sondando um dispositivo. Consulte também Gerenciando prioridades de hardware. Para obter informações específicas sobre IRQLs para rotinas de suporte específicas, consulte a página de referência da rotina.