Partilhar via


Usando a sincronização automática

Quase todo o código num driver baseado em framework reside em funções de retorno de chamada de evento. O framework sincroniza automaticamente a maioria das funções de callback de um driver, da seguinte maneira:

  • A estrutura sempre sincroniza objeto de dispositivo geral, objeto de dispositivo funcional (FDO)e objeto de dispositivo físico (PDO) funções de retorno de chamada de evento umas com as outras. Apenas uma das funções de callback pode ser chamada de cada vez para cada dispositivo. A exceção é EvtDeviceSurpriseRemoval, EvtDeviceQueryRemovee EvtDeviceQueryStop. Essas funções de retorno de chamada suportam eventos Plug and Play (PnP) e de gerenciamento de energia e são chamadas em IRQL = PASSIVE_LEVEL.

  • Opcionalmente, a estrutura pode sincronizar a execução das funções de retorno de chamada que lidam com as solicitações de E/S de um driver, para que essas funções de retorno de chamada sejam executadas uma de cada vez. Especificamente, a estrutura pode sincronizar as funções de retorno de chamada para fila , interrupção , chamada de procedimento adiado (DPC) , temporizador , item de trabalho e objetos de arquivo , juntamente com a função de retorno de chamada EvtRequestCancel do objeto de solicitação. A estrutura chama a maioria destas funções de retorno de chamada em IRQL = DISPATCH_LEVEL, mas pode-se forçar as funções de retorno de chamada relacionadas à fila e ao objeto de arquivo a serem executadas em IRQL = PASSIVE_LEVEL. (As funções de retorno de chamada de item de trabalho sempre são executadas em PASSIVE_LEVEL.)

A estrutura implementa essa sincronização automática usando um conjunto de bloqueios de sincronização internos. A estrutura garante que dois ou mais encadeamentos não consigam chamar a mesma função de retorno de chamada ao mesmo tempo. Cada thread deve esperar até que possa adquirir um bloqueio de sincronização antes de chamar uma função de callback. (Opcionalmente, os drivers também podem adquirir esses bloqueios de sincronização quando necessário. Para obter mais informações, consulte Usando bloqueios do Framework.)

O driver deve armazenar dados específicos do objeto no espaço de contexto do objeto . Se o driver usa apenas interfaces definidas pela estrutura, somente as funções de retorno de chamada que recebem um identificador para o objeto podem acessar esses dados. Se a estrutura estiver sincronizando chamadas para as funções de retorno de chamada do driver, apenas uma função de retorno de chamada será chamada de cada vez. O espaço de contexto do objeto é acessível a apenas uma função de retorno de chamada de cada vez.

A menos que o driver implemente tratamento de interrupção a nível passivo, o código que serve interrupções e acede a dados de interrupção deve ser executado no IRQL (DIRQL) do dispositivo e exige sincronização adicional. Para obter mais informações, consulte Código de Interrupção de Sincronização.

Se o driver habilitar a sincronização automática das funções de retorno de chamada que lidam com solicitações de E/S, a estrutura sincronizará essas funções de retorno de chamada para que sejam executadas uma de cada vez. A tabela a seguir lista as funções de retorno de chamada que a estrutura sincroniza.

Tipo de objeto Funções sincronizadas de retorno de chamada

Objeto de fila

manipuladores de solicitação, EvtIoQueueState, EvtIoResume, EvtIoStop

Objeto de arquivo

Todas as funções de retorno de chamada

Objeto de solicitação

EvtPedidoCancelar

Opcionalmente, a estrutura também pode sincronizar essas funções de retorno de chamada com qualquer função de retorno de chamada de objeto de interrupção, DPC, item de trabalho e objeto de temporizador que seu driver fornece para o dispositivo (excluindo a função de retorno de chamada EvtInterruptIsr do objeto de interrupção). Para habilitar esta sincronização adicional, o driver deve definir o AutomaticSerialization membro das estruturas de configuração desses objetos como TRUE.

Em resumo, o recurso de sincronização automática da estrutura fornece os seguintes recursos:

  • A estrutura sempre sincroniza as funções de retorno de chamada PnP e de gerenciamento de energia de cada dispositivo.

  • Opcionalmente, a estrutura de trabalho pode sincronizar os gestores de pedidos de uma fila de E/S e algumas funções adicionais de callback (consulte a tabela anterior).

  • Um driver pode solicitar que o framework sincronize funções de retorno de chamada para objetos de interrupção, DPC, item de trabalho e temporizador.

  • Os drivers devem sincronizar o código que lida com interrupções e acede a dados de interrupção usando as técnicas descritas em Sincronização de Código de Interrupção.

  • A estrutura não sincroniza as outras funções de retorno de chamada de um driver, como a função de retorno de chamada CompletionRoutine do driver ou as funções de retorno de chamada que o objeto de destino de E/S define. Em vez disso, o framework fornece bloqueios adicionais que os drivers podem usar para sincronizar essas funções de retorno de chamada.

Escolhendo um escopo de sincronização

Você pode optar por fazer com que a estrutura sincronize todas as funções de retorno de chamada associadas a todas as filas de E/S de um dispositivo. Como alternativa, você pode optar por fazer com que a estrutura sincronize separadamente as funções de retorno de chamada para cada uma das filas de E/S de um dispositivo. As opções de sincronização disponíveis para o driver são as seguintes:

  • Sincronização no nível do dispositivo

    A estrutura sincroniza as funções de retorno de chamada para todas as filas de E/S do dispositivo, garantindo que sejam executadas sequencialmente, uma de cada vez. A estrutura alcança essa sincronização ao adquirir o bloqueio de sincronização do dispositivo antes de chamar uma função de callback.

  • Sincronização no nível da fila

    O framework sincroniza as funções de retorno de chamada para cada fila de E/S individual, garantindo que sejam executadas uma de cada vez. A estrutura alcança essa sincronização adquirindo o bloqueio de sincronização da fila antes de chamar uma função callback.

  • Sem sincronização

    A estrutura não sincroniza a execução das funções de retorno de chamada na tabela anterior e não adquire um bloqueio de sincronização antes de chamar as funções de retorno de chamada. Se a sincronização for necessária, o driver deve fornecê-la.

Para especificar se você deseja que a estrutura forneça sincronização no nível do dispositivo, sincronização no nível da fila ou nenhuma sincronização para o driver, especifique um de escopo de sincronização para seu objeto de driver, objetos de dispositivo ou objetos de fila. O SynchronizationScope membro da estrutura WDF_OBJECT_ATTRIBUTES de um objeto identifica o escopo de sincronização do objeto. Os valores de escopo de sincronização que seu driver pode especificar são:

WdfSynchronizationScopeDevice
A estrutura sincroniza obtendo o bloqueio de sincronização de um objeto de dispositivo.

WdfSynchronizationScopeQueue
A estrutura sincroniza-se ao obter o bloqueio de sincronização de um objeto de fila.

WdfSynchronizationScopeNone
A estrutura não sincroniza e não obtém um bloqueio de sincronização.

WdfSynchronizationScopeInheritFromParent
A estrutura obtém o valor SynchronizationScope a partir do objeto pai do objeto em questão.

Em geral, evite usar a sincronização no nível do dispositivo.

Para obter mais informações sobre os valores de escopo de sincronização, consulte WDF_SYNCHRONIZATION_SCOPE.

O escopo de sincronização padrão para objetos de driver é WdfSynchronizationScopeNone. O escopo de sincronização padrão para objetos de dispositivo e fila é WdfSynchronizationScopeInheritFromParent.

Para usar a estrutura para fornecer sincronização ao nível do dispositivo para todos os dispositivos, defina SynchronizationScope como WdfSynchronizationScopeDevice na estrutura WDF_OBJECT_ATTRIBUTES do objeto de driver. Use o valor padrão WdfSynchronizationScopeInheritFromParent para cada objeto de dispositivo .

Para fornecer sincronização no nível do dispositivo para dispositivos individuais, use o padrão WdfSynchronizationScopeNone valor para o driver objeto. Defina SynchronizationScope para WdfSynchronizationScopeDevice na estrutura WDF_OBJECT_ATTRIBUTES dos objetos de dispositivos individuais.

Se desejar que a estrutura forneça sincronização no nível da fila para um dispositivo, você pode usar as seguintes técnicas:

  • Para versões de estrutura 1.9 e posteriores, habilite a sincronização no nível de fila para filas individuais definindo WdfSynchronizationScopeQueue na estrutura WDF_OBJECT_ATTRIBUTES do objeto de fila. Esta técnica é preferida.

  • Como alternativa, você pode usar as seguintes etapas em todas as versões do framework:

    1. Defina SynchronizationScope como WdfSynchronizationScopeQueue na estrutura WDF_OBJECT_ATTRIBUTES do objeto do dispositivo .
    2. Use o padrão WdfSynchronizationScopeInheritFromParent valor para a fila de objetos de cada dispositivo.

Se você não quiser que a estrutura sincronize as funções de retorno de chamada que lidam com as solicitações de E/S do driver, use o valor de padrão SynchronizationScope para os objetos de driver, dispositivo e fila do driver. Nesse caso, o framework não sincroniza automaticamente as funções de retorno de chamada relacionadas à solicitação de E/S do driver. A estrutura pode invocar as funções de callback no nível IRQL <= DISPATCH_LEVEL.

Definir um valor SynchronizationScope sincroniza apenas as funções de callback que a tabela anterior contém. Se quiseres que o framework também sincronize as funções de retorno de chamada de interrupção, DPC, item de trabalho e temporizador do driver, define o AutomaticSerialization membro das estruturas de configuração destes objetos como TRUE.

No entanto, você pode definir AutomaticSerialization como TRUE somente se todas as funções de retorno que você deseja sincronizar sejam executadas no mesmo IRQL. Escolher um nível de execução , que é descrito a seguir, pode resultar em níveis de IRQL incompatíveis. Em tal situação, o driver deve usar bloqueios do framework em vez de definir AutomaticSerialization. Para obter mais informações sobre as estruturas de configuração para objetos de interrupção, DPC, item de trabalho e temporizador, e para obter mais informações sobre restrições que se aplicam à configuração AutomaticSerialization nessas estruturas, consulte WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIGe WDF_TIMER_CONFIG.

Caso defina AutomaticSerialization como TRUE, selecione a sincronização a nível de fila.

Escolher um nível de execução

Quando um driver cria alguns tipos de objetos de estrutura, ele pode especificar um nível de execução para o objeto. O nível de execução especifica o IRQL no qual a estrutura invoca as funções de retorno de chamada de eventos do objeto que processam solicitações de E/S do driver.

Se um driver fornecer um nível de execução, o nível fornecido afetará as funções de callback para os objetos de fila e arquivo. Normalmente, se o driver usa sincronização automática, o framework chama essas funções de retorno de chamada em IRQL = DISPATCH_LEVEL. Ao especificar um nível de execução, o driver pode forçar a estrutura a chamar essas funções de retorno de chamada em IRQL = PASSIVE_LEVEL. A estrutura usa as seguintes regras ao definir o IRQL no qual chama as funções de retorno de chamada de fila e objeto de arquivo:

  • Se um driver utiliza a sincronização automática, o framework chama as funções de retorno de chamada de fila e de objeto de arquivo no IRQL = DISPATCH_LEVEL, a menos que o driver solicite ao framework que chame as suas funções de retorno de chamada no IRQL = PASSIVE_LEVEL.

  • Se um driver não usar a sincronização automática e não especificar um nível de execução, a estrutura poderá chamar as funções de retorno de chamada de fila e objeto de arquivo do driver em IRQL <= DISPATCH_LEVEL.

Se o driver fornecer funções de retorno de chamada de objeto de arquivo, você provavelmente deseja que a estrutura chame essas funções de retorno de chamada em IRQL = PASSIVE_LEVEL porque alguns dados de arquivo, como o nome do arquivo, podem ser pagináveis.

Para fornecer um nível de execução, o driver deve especificar um valor para o ExecutionLevel membro da estrutura WDF_OBJECT_ATTRIBUTES de um objeto. Os valores de nível de execução que seu driver pode especificar são:

WdfExecutionLevelPassive
A estrutura chama, quando IRQL = PASSIVE_LEVEL, as funções de retorno de chamada do objeto.

WdfExecutionLevelDispatch
O framework pode chamar as funções de callback do objeto em IRQL <= DISPATCH_LEVEL. Se o driver utilizar sincronização automática, o framework chamará sempre as funções de retorno de chamada em IRQL = DISPATCH_LEVEL.

WdfExecutionLevelInheritFromParent
A estrutura obtém o valor de ExecutionLevel do objeto do pai.

O nível de execução padrão para objetos de driver é WdfExecutionLevelDispatch. O nível de execução padrão para todos os outros objetos é WdfExecutionLevelInheritFromParent.

Para obter mais informações sobre os valores de nível de execução, consulte WDF_EXECUTION_LEVEL.

A tabela a seguir mostra o nível de IRQL no qual a estrutura pode chamar as funções de retorno de chamada de um driver para objetos de fila e objetos de arquivo.

Escopo de sincronização Nível de Execução IRQL de Funções de Retorno de Chamada de Fila e Arquivo

WdfSynchronizationScopeDevice

NívelDeExecuçãoWdfPassivo

NÍVEL_PASSIVO

WdfSynchronizationScopeDevice

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeQueue

NívelDeExecuçãoWdfPassivo

NÍVEL_PASSIVO

WdfSynchronizationScopeQueue

WdfExecutionLevelDispatch

DISPATCH_LEVEL

WdfSynchronizationScopeNone

NívelDeExecuçãoWdfPassivo

NÍVEL_PASSIVO

WdfSynchronizationScopeNone

WdfExecutionLevelDispatch

<= NÍVEL_DE_DESPACHO

Você pode definir o nível de execução como WdfExecutionLevelPassive ou WdfExecutionLevelDispatch para driver, dispositivo, arquivo, fila, temporizador e objetos gerais. Para outros objetos, você só pode definir WdfExecutionLevelInheritFromParent.

Especifique WdfExecutionLevelPassive se:

  • As funções de retorno de chamada do driver devem chamar métodos de estrutura ou rotinas WDM (Windows Driver Model) que você pode chamar somente em IRQL = PASSIVE_LEVEL.

  • As funções de callback do driver devem acessar o código ou dados que podem ser paginados. Por exemplo, as funções callback de objeto de ficheiro normalmente acessam dados pagináveis.

Em vez de definir WdfExecutionLevelPassive, o seu driver pode definir WdfExecutionLevelDispatch e fornecer uma função callback que cria itens de trabalho se ele precisar lidar com algumas operações em IRQL = PASSIVE_LEVEL.

Antes de decidir se o seu driver deve definir o nível de execução de um objeto como WdfExecutionLevelPassive, determine o IRQL em que o seu driver e outros drivers da pilha de drivers são chamados. Considere as seguintes situações:

  • Se o driver estiver na parte superior da pilha de drivers do modo kernel, o sistema normalmente chamará o driver em IRQL = PASSIVE_LEVEL. O cliente de tal driver pode ser um driver baseado em UMDF ou um aplicativo de modo de usuário. Especificar WdfExecutionLevelPassive não afeta negativamente o desempenho do driver, porque o framework não precisa enfileirar as chamadas do driver para itens de trabalho que ocorrem em IRQL = PASSIVE_LEVEL.

  • Se o driver não estiver no topo da pilha, é provável que o sistema não chame o seu driver com IRQL = PASSIVE_LEVEL. Portanto, a estrutura deve enfileirar as chamadas do driver para itens de trabalho, que são posteriormente executados em IRQL = PASSIVE_LEVEL. Esse processo pode causar um desempenho ruim do driver, em comparação com permitir que as funções de retorno de chamada do driver sejam chamadas no IRQL <= DISPATCH_LEVEL.

Para objetos DPC e para objetos de timer que não representam temporizadores de nível passivo, não é possível definir o membro AutomaticSerialization da estrutura de configuração para TRUE se definir o nível de execução do dispositivo pai para WdfExecutionLevelPassive. A estrutura adquire os bloqueios de sincronização de retorno de chamada do objeto de dispositivo em IRQL = PASSIVE_LEVEL. Portanto, ele não pode usar os bloqueios para sincronizar as funções de retorno de chamada do objeto DPC ou do temporizador, que devem ser executadas em IRQL = DISPATCH_LEVEL. Nesse caso, o seu driver deve usar bloqueios de rotação da estrutura em qualquer dispositivo, DPC ou funções de retorno de chamada de objetos temporizadores que precisam ser sincronizadas entre si.

Observe também que, para objetos de timer que representam temporizadores de nível passivo, você pode definir o AutomaticSerialization membro da estrutura de configuração como TRUE apenas se o nível de execução do dispositivo pai estiver definido como WdfExecutionLevelPassive.