Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Quase todo o código de um driver baseado em framework reside em funções de callback de evento. O framework sincroniza automaticamente a maioria das funções de callback de um driver da seguinte maneira:
A estrutura sempre sincroniza o objeto de dispositivo geral, o FDO (objeto de dispositivo funcional) e as funções de retorno de chamada de evento PDO (objeto de dispositivo físico) entre si. Somente uma das funções de retorno de chamada pode ser chamada por vez para cada dispositivo. A exceção é EvtDeviceSurpriseRemoval, EvtDeviceQueryRemove e EvtDeviceQueryStop. Essas funções de retorno de chamada dão suporte a eventos plug and play (PnP) e 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, de modo que essas funções de retorno de chamada executem 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 e item de trabalho, além de objetos de arquivo, juntamente com a função de retorno de chamada EvtRequestCancel do objeto de solicitação. A estrutura chama a maioria dessas funções de retorno de chamada em IRQL = DISPATCH_LEVEL, mas você pode forçar as funções de retorno de chamada relacionadas à fila e ao objeto de arquivo para serem executadas em IRQL = PASSIVE_LEVEL. (As funções de callback do 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 threads não possam chamar a mesma função de retorno de chamada ao mesmo tempo. Cada thread deve aguardar até que possa adquirir um bloqueio de sincronização antes de chamar uma função de retorno de chamada. (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 usar apenas interfaces definidas pela estrutura, somente as funções de retorno de chamada que recebem um identificador para o objeto poderão 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 por vez. O espaço de contexto do objeto é acessível a apenas uma função de retorno de chamada por vez.
A menos que o driver implemente o tratamento de interrupção de nível passivo, o código que atende às interrupções e acessa os dados de interrupção deve ser executado no DIRQL do dispositivo e requer sincronização adicional. Para obter mais informações, consulte Sincronizando o código de interrupçã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 sincroniza essas funções de retorno de chamada para que elas 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 de callback sincronizadas |
|---|---|
Objeto Queue |
Manipuladores de solicitação, EvtIoQueueState, EvtIoResume, EvtIoStop |
Objeto de arquivo |
Todas as funções de retorno de chamada |
Objeto da solicitação |
Opcionalmente, a estrutura também pode sincronizar essas funções de retorno de chamada com qualquer interrupção, DPC, item de trabalho e funções de retorno de chamada de objeto de temporizador que o driver fornece para o dispositivo (excluindo a função de retorno de chamada EvtInterruptIsr do objeto de interrupção). Para habilitar essa sincronização adicional, o driver deve definir o membro de AutomaticSerialization das estruturas de configuração desses objetos como TRUE.
Em resumo, a funcionalidade de sincronização automática da estrutura fornece os seguintes recursos:
A estrutura sempre sincroniza as funções de retorno de chamada de PnP e de gerenciamento de energia de cada dispositivo.
Opcionalmente, a estrutura pode sincronizar os manipuladores de solicitação de uma fila de E/S e algumas funções de retorno de chamada adicionais (consulte a tabela anterior).
Um driver pode pedir ao framework para sincronizar funções de callback para objetos de interrupção, DPC, item de trabalho e temporizador.
Os drivers devem sincronizar o código que atendem a interrupções e acessam dados de interrupção usando as técnicas descritas em Sincronização de Código de Interrupção.
A estrutura não sincroniza 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 definidas pelo objeto de destino de E/S. Em vez disso, o framework fornece bloqueios adicionais que os drivers podem usar para sincronizar essas funções de callback.
Selecionando o 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 que estão disponíveis para o driver são as seguintes:
Sincronização no nível do dispositivo
O framework sincroniza as funções de retorno de chamada para todas as filas de E/S do dispositivo, garantindo que elas executem de forma sequencial. O framework alcança essa sincronização adquirindo o bloqueio de sincronização do dispositivo antes de chamar um callback.
Sincronização no nível da fila
O framework sincroniza as funções de retorno de chamada de cada fila de E/S individual, permitindo que elas executem uma de cada vez. O framework obtém 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 escopo de sincronização para o objeto de driver, objetos de dispositivo ou objetos de fila. O membro SynchronizationScope da estrutura WDF_OBJECT_ATTRIBUTES de um objeto identifica o escopo de sincronização do objeto. Os valores de escopo de sincronização que o driver pode especificar são:
WdfSynchronizationScopeDevice
O framework sincroniza ao obter o bloqueio de sincronização de um objeto de dispositivo.
WdfSynchronizationScopeQueue
A estrutura sincroniza 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 do objeto a partir do objeto pai.
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 no nível do dispositivo para todos os dispositivos, defina SynchronizationScope como WdfSynchronizationScopeDevice na estrutura WDF_OBJECT_ATTRIBUTES do objeto de driver do 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 valor WdfSynchronizationScopeNone padrão para o objeto driver . Defina SynchronizationScope como WdfSynchronizationScopeDevice na estrutura WDF_OBJECT_ATTRIBUTES de objetos de dispositivo individuais.
Se você quiser que a estrutura forneça sincronização no nível da fila para um dispositivo, use as seguintes técnicas:
Para versões de estrutura 1.9 e posteriores, habilite a sincronização no nível da fila para filas individuais definindo WdfSynchronizationScopeQueue na estrutura WDF_OBJECT_ATTRIBUTES do objeto de fila. Essa técnica é preferida.
Como alternativa, você pode usar as seguintes etapas em todas as versões da estrutura:
- Defina SynchronizationScope como WdfSynchronizationScopeQueue na estrutura WDF_OBJECT_ATTRIBUTES do objeto do dispositivo .
- Use o valor padrão WdfSynchronizationScopeInheritFromParent para os objetos de fila de cada um dos dispositivos.
Se você não quiser que o framework sincronize as funções de retorno de chamada que lidam com as solicitações de E/S do seu driver, use o valor padrão de SynchronizationScope para os objetos do driver, do dispositivo e da fila. Nesse caso, o framework não sincroniza automaticamente as funções de retorno de chamada relacionadas à solicitação de E/S do driver. O framework pode chamar as funções de callback em IRQL <= DISPATCH_LEVEL.
Definir um valor SynchronizationScope sincroniza apenas as funções de retorno de chamada que a tabela anterior contém. Se você quiser que a estrutura também sincronize as funções de interrupção, DPC, item de trabalho e retorno de chamada de objeto do temporizador do driver, defina o membro de AutomaticSerialization das estruturas de configuração desses objetos como TRUE.
No entanto, você poderá definir AutomaticSerialization como TRUE somente se todas as funções de retorno de chamada que você deseja sincronizar forem executadas no mesmo IRQL. Escolher um nível de execução, que é descrito em seguida, pode resultar em níveis IRQL incompatíveis. Nessa situação, o driver deve usar bloqueios do framework em vez de definir o 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 da AutomaticSerialization nessas estruturas, consulte WDF_INTERRUPT_CONFIG, WDF_DPC_CONFIG, WDF_WORKITEM_CONFIG e WDF_TIMER_CONFIG.
Se você definir AutomaticSerialization como TRUE, selecione a sincronização no nível da fila.
Escolhendo 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 chama as funções de retorno de chamada de evento do objeto que manipulam as solicitações de E/S de um driver.
Se um driver fornecer um nível de execução, esse nível afeta imediatamente as funções de callback para objetos de fila e de arquivo. Normalmente, se o driver usa a 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 o framework 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 ela chama funções de retorno de chamada de fila e de objeto de arquivo:
Se um driver usar a sincronização automática, a estrutura chamará das funções de retorno de chamada do objeto de fila e do objeto de arquivo em IRQL = DISPATCH_LEVEL, a menos que o driver peça à estrutura para chamar suas funções de retorno de chamada em IRQL = PASSIVE_LEVEL.
Se um driver não usar sincronização automática e não especificar um nível de execução, o framework poderá chamar as funções de retorno de chamada de fila e de objeto de arquivo do driver em IRQL <= DISPATCH_LEVEL.
Se o driver fornecer funções de retorno de chamada de objeto de arquivo, você muito provavelmente desejará 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, são pagináveis.
Para fornecer um nível de execução, o driver deve especificar um valor para o membro ExecutionLevel da estrutura WDF_OBJECT_ATTRIBUTES de um objeto. Os valores de nível de execução que o driver pode especificar são:
WdfExecutionLevelPassive (nível de execução passivo do Wdf)
O framework chama as funções de retorno de chamada do objeto quando o IRQL está em nível PASSIVE_LEVEL.
WdfExecutionLevelDispatch
A estrutura pode chamar as funções callback do objeto em IRQL <= DISPATCH_LEVEL. Se o driver usar a sincronização automática, a estrutura sempre chamará as funções de retorno de chamada em IRQL = DISPATCH_LEVEL.
WdfExecutionLevelInheritFromParent
A estrutura obtém o valor ExecutionLevel do objeto a partir do pai do objeto.
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 IRQL em que o framework pode chamar as funções de retorno de chamada do driver para objetos de fila e objetos de arquivo.
| Escopo de sincronização | Nível de execução | Nível de Pedido de Interrupção (IRQL) para Funções de Retorno de Chamada de Fila e Arquivo |
|---|---|---|
WdfSynchronizationScopeDevice |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeDevice |
WdfExecutionLevelDispatch |
DISPATCH_LEVEL |
WdfSynchronizationScopeQueue |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeQueue |
WdfExecutionLevelDispatch |
DISPATCH_LEVEL |
WdfSynchronizationScopeNone |
WdfExecutionLevelPassive |
PASSIVE_LEVEL |
WdfSynchronizationScopeNone |
WdfExecutionLevelDispatch |
<= DISPATCH_LEVEL |
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 do WDM (Modelo de Driver do Windows) que você pode chamar apenas em IRQL = PASSIVE_LEVEL.
As funções de callback do driver devem acessar código ou dados que podem ser paginados. Por exemplo, funções de callback de objetos de arquivo normalmente acessam dados pagináveis.
Em vez de definir WdfExecutionLevelPassive, seu driver pode definir WdfExecutionLevelDispatch e fornecer uma função de retorno de chamada que cria itens de trabalho se 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 nível de IRQL no qual seu driver e outros controladores na pilha de controladores são chamados. Considere as seguintes situações:
Se o driver estiver na parte superior da pilha de driver no modo kernel, o sistema normalmente chamará o driver em IRQL = PASSIVE_LEVEL. O cliente desse driver pode ser um driver baseado em UMDF ou um aplicativo de modo de usuário. Especificar WdfExecutionLevelPassive não impacta negativamente o desempenho do driver, pois o framework não precisa enfileirar as chamadas do driver para itens de trabalho chamados em IRQL = PASSIVE_LEVEL.
Se o driver não estiver na parte superior da pilha, o sistema provavelmente não chamará seu driver em IRQL = PASSIVE_LEVEL. Portanto, a estrutura deve enfileirar as chamadas do driver para itens de trabalho, que mais tarde são chamados 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 em IRQL <= DISPATCH_LEVEL.
Para objetos DPC e para objetos de temporizador que não representam temporizadores de nível passivo, não é possível definir o membro de AutomaticSerialization da estrutura de configuração como TRUE se você definir o nível de execução do dispositivo pai como WdfExecutionLevelPassive. A estrutura adquire os callback synchronization locks 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 de objeto DPC ou timer, que devem ser executadas em IRQL = DISPATCH_LEVEL. Nesse caso, o driver deve usar spin locks do framework em funções de callback de objetos de dispositivo, DPC ou temporizador que devem ser sincronizadas entre si.
Observe também que, para objetos de temporizador que representam temporizadores de nível passivo, você pode definir o membro automaticSerialization da estrutura de configuração como TRUE somente se o nível de execução do dispositivo pai estiver definido como WdfExecutionLevelPassive.