Acessando informações de estado compartilhado
Use as seguintes diretrizes gerais para projetar e gravar rotinas synchCritSection que mantêm o estado:
Para acessar dados que um ISR também acessa, uma rotina de driver deve chamar uma rotina SynchCritSection . O código de seção não crítico pode ser interrompido. Lembre-se de que não é suficiente simplesmente adquirir um bloqueio de rotação para proteger os dados que os ISRs também acessam, pois os ISRs são executados no DIRQL e a aquisição de um bloqueio de rotação (KeAcquireSpinLock) gera apenas IRQL para DISPATCH_LEVEL, o que permite uma interrupção para invocar o ISR no processador atual.
Dê a cada rotina SynchCritSection que mantém a responsabilidade de informações de estado por um conjunto discreto de variáveis de estado. Ou seja, evite escrever rotinas synchCritSection que mantêm informações de estado sobrepostas.
Isso impede a contenção e, possivelmente, as condições de corrida, entre as rotinas synchCritSection (e o ISR) tentando acessar o mesmo estado simultaneamente.
Isso também garante que cada rotina SynchCritSection retorne o controle o mais rápido possível porque uma rotina SynchCritSection nunca precisa esperar por outra que atualize algumas das mesmas informações de estado para retornar o controle.
Evite escrever uma rotina SynchCritSection única, grande e de uso geral que faça mais testes de condições para determinar o que fazer do que realmente fazer um trabalho útil. Por outro lado, evite ter muitas rotinas SynchCritSection que nunca executam uma instrução condicional porque cada uma atualiza apenas um único byte de informações de estado.
Cada rotina SynchCritSection deve retornar o controle o mais rápido possível, pois a execução de qualquer rotina SynchCritSection impede a execução do ISR do driver.
A seguir está uma técnica para manter um contador de temporizador em uma extensão de dispositivo. Suponha que o driver use o contador para determinar se uma operação de E/S atingiu o tempo limite. Suponha também que o driver não sobreponha as operações de E/S.
A rotina StartIo do driver inicializa o contador de temporizador para algum valor inicial para cada solicitação de E/S. Em seguida, o driver adiciona um segundo ao valor de tempo limite do dispositivo, caso sua rotina IoTimer tenha retornado o controle.
O ISR do driver deve definir esse contador de temporizador como menos um.
A rotina IoTimer do driver é chamada uma vez por segundo para ler o contador de tempo e determinar se o ISR já a definiu como menos uma. Caso contrário, a rotina IoTimer diminui o contador usando KeSynchronizeExecution para chamar uma rotina de SynchCritSection_1.
Se o contador for para zero, indicando que a solicitação atingiu o tempo limite, a rotina SynchCritSection_1 chamará uma rotina de SynchCritSection_2 para programar uma operação de redefinição de dispositivo. Se o contador for menos um, a rotina IoTimer simplesmente retornará.
Se a rotina DpcForIsr do driver precisar reprogramar o dispositivo para iniciar uma operação de transferência parcial, ele deverá reinicializar o contador de temporizador como a rotina StartIo fez.
A rotina DpcForIsr também deve usar KeSynchronizeExecution para chamar a rotina de SynchCritSection_2, ou possivelmente uma rotina SynchCritSection_3, para programar o dispositivo para outra operação de transferência.
Nesse cenário, o driver tem mais de uma rotina SynchCritSection , cada uma com responsabilidades discretas e específicas; um para manter seu contador de temporizador e um ou mais outros para programar o dispositivo. Cada rotina SynchCritSection pode retornar o controle rapidamente porque executa uma única tarefa discreta.
Observe que o driver tem uma única rotina SynchCritSection_1 que, juntamente com o ISR do driver, mantém o estado no contador de temporizador. Portanto, não há contenção para acesso ao contador de temporizador entre várias rotinas SynchCritSection e o ISR.