Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Minimizar o tempo que um controlador mantém bloqueios de rotação pode melhorar significativamente o desempenho do controlador e do sistema em geral. Por exemplo, considere a figura a seguir, que mostra como um bloqueio de rotação de interrupção protege dados específicos do dispositivo que devem ser compartilhados entre um ISR e as rotinas StartIo e DpcForIsr em uma máquina SMP.
Enquanto o ISR do driver é executado em DIRQL em um processador, sua rotina StartIo é executada em DISPATCH_LEVEL em um segundo processador. O manipulador de interrupção do kernel mantém o InterruptSpinLock para o ISR do driver, que acessa dados protegidos específicos do dispositivo, como estado ou ponteiros para registros de dispositivo (SynchronizeContext), na extensão de dispositivo do driver. A rotina StartIo , que está pronta para acessar SynchronizeContext, chama KeSynchronizeExecution, passando um ponteiro para os objetos de interrupção associados, o SynchronizeContext compartilhado e a rotina SynchCritSection do driver (AccessDevice na figura anterior).
Até que o ISR retorne, liberando assim o InterruptSpinLock do driver, KeSynchronizeExecutiongira no segundo processador, impedindo AccessDevice de tocar em SynchronizeContext. No entanto, KeSynchronizeExecution também eleva o IRQL no segundo processador até o SynchronizeIrql dos objetos de interrupção, evitando assim que outra interrupção de dispositivo ocorra nesse processador, para que o AccessDevice possa ser executado em DIRQL assim que o ISR retornar. No entanto, as interrupções DIRQL mais altas para outros dispositivos, bem como as interrupções do relógio e as interrupções de falha de energia, ainda podem ocorrer em qualquer processador.
Quando o ISR enfileira o DpcForIsr do driver e retorna, AccessDevice é executado no segundo processador no SynchronizeIrql dos objetos de interrupção associados e acessa SynchronizeContext. Enquanto isso, o DpcForIsr é executado em outro processador em DISPATCH_LEVEL IRQL. O DpcForIsr também está pronto para acessar SynchronizeContext, por isso chama KeSynchronizeExecution usando os mesmos parâmetros que a rotina StartIo fez na Etapa 1.
Quando KeSynchronizeExecution adquire o bloqueio de rotação e executa AccessDevice em nome da rotina StartIo , a rotina de sincronização fornecida pelo driver AccessDevice recebe acesso exclusivo a SynchronizeContext. Como AccessDevice é executado no SynchronizeIrql, o ISR do driver não pode adquirir o bloqueio de rotação e acessar a mesma área até que o bloqueio de rotação seja liberado, mesmo se o dispositivo interromper em outro processador enquanto AccessDevice está em execução.
AccessDevice retorna, liberando o bloqueio de rotação. A rotina StartIo retoma a execução em DISPATCH_LEVEL no segundo processador. KeSynchronizeExecution agora executa AccessDevice no terceiro processador, para que ele possa acessar SynchronizeContext em nome do DpcForIsr. No entanto, se uma interrupção de dispositivo tiver ocorrido antes de o DpcForIsr chamar o KeSynchronizeExecution na Etapa 2, o ISR pode ser executado em outro processador antes de o KeSynchronizeExecution poder adquirir o spin lock e executar o AccessDevice no terceiro processador.
Como mostra a figura anterior, enquanto uma rotina executada em um processador mantém um bloqueio de rotação, todas as outras rotinas que tentam adquirir esse bloqueio de rotação não são executadas. Cada rotina que tenta adquirir um bloqueio de rotação já mantido simplesmente gira em seu processador atual até que o titular libere o bloqueio de rotação. Quando um spin lock é liberado, uma e somente uma rotina pode adquiri-lo; todas as outras rotinas atualmente tentando adquirir o mesmo spin lock continuarão a rodar.
O detentor de qualquer bloqueio de rotação opera a um IRQL elevado: a DISPATCH_LEVEL para um bloqueio de rotação executivo ou a um DIRQL para um bloqueio de rotação de interrupção. Os chamadores de KeAcquireSpinLock e KeAcquireInStackQueuedSpinLock operam em DISPATCH_LEVEL até que chamem KeReleaseSpinLock ou KeReleaseInStackQueuedSpinLock para liberar o bloqueio. Os chamadores de KeSynchronizeExecution elevam automaticamente o IRQL no processador atual para o SynchronizeIrql dos objetos de interrupção até que a rotina SynchCritSection fornecida pelo chamador saia e KeSynchronizeExecution retorne o controle. Para obter mais informações, consulte Chamando rotinas de suporte que usam bloqueios de rotação.
Tenha em mente o seguinte fato sobre o uso de bloqueios giratórios:
Todo o código que é executado em um IRQL mais baixo não pode fazer qualquer trabalho no conjunto de processadores ocupados por um suporte de spin-lock e por outras rotinas tentando adquirir o mesmo spin lock.
Consequentemente, minimizar o tempo que um motorista mantém os bloqueios de rotação resulta em um desempenho significativamente melhor do motorista e contribui significativamente para um melhor desempenho geral do sistema.
Como mostra a figura anterior, o manipulador de interrupção do kernel executa rotinas em execução no mesmo IRQL numa máquina multiprocessadora numa base de primeira entrada, primeira saída. O kernel também faz o seguinte:
Quando uma rotina de driver chama KeSynchronizeExecution, o kernel faz com que a rotina SynchCritSection do driver seja executada no mesmo processador do qual ocorreu a chamada para KeSynchronizeExecution (consulte as etapas 1 e 3).
Quando o ISR de um driver enfileira seu DpcForIsr, o kernel faz com que o DPC seja executado no primeiro processador disponível no qual o IRQL fica abaixo de DISPATCH_LEVEL. Este não é necessariamente o mesmo processador a partir do qual a chamada IoRequestDpc ocorreu (consulte a Etapa 2).
As operações de E/S controladas por interrupção de um driver tendem a ser serializadas numa máquina uniprocessador, mas as mesmas operações podem ser verdadeiramente assíncronas numa máquina SMP. Como mostra a figura anterior, o ISR de um driver pode ser executado em CPU4 numa máquina SMP antes de o seu DpcForIsr começar a processar um IRP para o qual o ISR já manipulou uma interrupção de dispositivo na CPU1.
Em outras palavras, não se deve presumir que um spin lock de interrupção pode proteger dados específicos da operação que o ISR salva quando é executado em um processador, de serem sobrescritos pelo ISR ao ocorrer uma interrupção de dispositivo em outro processador, antes da execução da rotina DpcForIsr ou CustomDpc.
Embora um driver pudesse tentar serializar todas as operações de E/S controladas por interrupção para preservar os dados coletados pelo ISR, esse driver não seria executado muito mais rápido em uma máquina SMP do que em uma máquina uniprocessador. Para obter o melhor desempenho possível do driver enquanto permanece portável em plataformas de uniprocessador e multiprocessador, os drivers devem usar uma técnica alternativa para guardar os dados específicos de operação obtidos pelo ISR para processamento subsequente pelo DpcForIsr.
Por exemplo, um ISR pode salvar dados específicos da operação no IRP que passa para o DpcForIsr. Um refinamento dessa técnica é implementar um DpcForIsr que consulta uma contagem aumentada por ISR, processa o número de IRPs da contagem usando dados fornecidos por ISR e redefine a contagem para zero antes de retornar. É claro que a contagem deve ser protegida pelo bloqueio de rotação de interrupção do driver porque seu ISR e um SynchCritSection manteriam seu valor dinamicamente.