Condividi tramite


Uso di blocchi spin: esempio

Riducendo al minimo il tempo in cui un driver contiene blocchi di rotazione può migliorare significativamente le prestazioni del driver e del sistema in generale. Si consideri ad esempio la figura seguente, che illustra come un blocco di spin di interruzione protegge i dati specifici del dispositivo che devono essere condivisi tra un ISR e le routine StartIo e DpcForIsr in un computer SMP.

diagramma che illustra l'uso di un blocco di spin di interruzione.

  1. Mentre l'ISR del driver viene eseguito in DIRQL su un processore, la routine StartIo viene eseguita in DISPATCH_LEVEL su un secondo processore. Il gestore di interruzioni del kernel contiene l'interruptSpinLock per l'ISR del driver, che accede a dati protetti, specifici del dispositivo, ad esempio lo stato o i puntatori ai registri dei dispositivi (SyncContext), nell'estensione del dispositivo del driver. La routine StartIo , pronta per accedere a SyncContext, chiama KeSynchronizeExecution, passando un puntatore agli oggetti di interruzione associati, il synccontext condiviso e la routine SynchCritSection del driver (AccessDevice nella figura precedente).

    Fino a quando l'ISR non viene restituito, rilasciando così l'InterruptSpinLock del driver, KeSynchronizeExecution sul secondo processore, impedendo che AccessDevice tocco SyncContext. Tuttavia, KeSynchronizeExecution genera anche IRQL nel secondo processore all'oggetto SyncIrql degli oggetti di interruzione, impedendo così che si verifichi un altro interruzione del dispositivo nel processore in modo che AccessDevice possa essere eseguito a DIRQL non appena restituisce l'ISR. Tuttavia, gli interruzioni DIRQL più elevate per altri dispositivi, gli interruzioni dell'orologio e gli interruzioni di alimentazione possono comunque verificarsi in entrambi i processori.

  2. Quando l'ISR accoda il DpcForIsr del driver e restituisce, AccessDevice viene eseguito nel secondo processore in corrispondenza dell'oggetto SyncIrql degli oggetti di interruzione associati e accede a SyncContext. Nel frattempo, il DpcForIsr viene eseguito su un altro processore in DISPATCH_LEVEL IRQL. DpcForIsr è anche pronto per accedere a SyncContext, quindi chiama KeSynchronizeExecution usando gli stessi parametri che la routine StartIo ha eseguito nel passaggio 1.

    Quando KeSynchronizeExecution acquisisce il blocco di spin ed esegue AccessDevice per conto della routine StartIo , la routine di sincronizzazione fornita dal driver AccessDevice viene concessa l'accesso esclusivo a SyncContext. Poiché AccessDevice viene eseguito in SyncIrql, l'ISR del driver non può acquisire il blocco di spin e accedere alla stessa area fino a quando non viene rilasciato il blocco spin, anche se il dispositivo interrompe un altro processore mentre AccessDevice è in esecuzione.

  3. AccessDevice restituisce, rilasciando il blocco di rotazione. La routine StartIo riprende l'esecuzione in DISPATCH_LEVEL nel secondo processore. KeSynchronizeExecution esegue ora AccessDevice nel terzo processore, in modo che possa accedere a SyncContext per conto del DpcForIsr. Tuttavia, se si è verificato un interruzione del dispositivo prima che il DpcForIsr chiamato KeSynchronizeExecution nel passaggio 2, l'ISR potrebbe essere eseguito in un altro processore prima che KeSynchronizeExecution possa acquisire il blocco spin ed eseguire AccessDevice nel terzo processore.

Come illustrato nella figura precedente, mentre una routine in esecuzione su un processore contiene un blocco di rotazione, ogni altra routine che tenta di acquisire tale blocco di spin non ottiene alcun lavoro fatto. Ogni routine che tenta di acquisire un blocco di rotazione già mantenuto semplicemente gira sul processore corrente fino a quando il titolare rilascia il blocco di rotazione. Quando viene rilasciato un blocco di rotazione, una e una sola routine può acquisirla; ogni altra routine attualmente cercando di acquisire lo stesso blocco di spin continuerà a girare.

Il titolare di qualsiasi blocco di spin viene eseguito in un IRQL generato: in DISPATCH_LEVEL per un blocco spin esecutivo o in un blocco DIRQL per un blocco di spin spin di interruzione. I chiamanti di KeAcquireSpinLock e KeAcquireInStackQueuedSpinLock vengono eseguiti in DISPATCH_LEVEL finché non chiamano KeReleaseSpinLock o KeReleaseInStackQueuedSpinLock per rilasciare il blocco. I chiamanti di KeSynchronizeExecution generano automaticamente IRQL nel processore corrente nell'oggetto SyncIrql degli oggetti interrupt fino a quando la routine SynchCritSection fornita dal chiamante termina e KeSynchronizeExecution restituisce il controllo. Per altre informazioni, vedere Chiamate di routine di supporto che usano blocchi spin.

Tenere presente il fatto seguente sull'uso di blocchi di rotazione:

Tutto il codice eseguito in un irQL inferiore non può ottenere alcun lavoro eseguito sul set di processori occupati da un titolare di blocco spin e da altre routine che tentano di acquisire lo stesso blocco di rotazione.

Di conseguenza, ridurre al minimo il tempo in cui un driver contiene blocchi di rotazione comporta prestazioni del driver significativamente migliori e contribuisce in modo significativo a migliorare le prestazioni complessive del sistema.

Come illustrato nella figura precedente, il gestore di interruzioni del kernel esegue routine eseguite nello stesso IRQL in un computer multiprocessore in base a un primo servizio. Il kernel esegue anche quanto segue:

  • Quando una routine driver chiama KeSynchronizeExecution, il kernel causa l'esecuzione della routine SynchCritSection del driver nello stesso processore da cui si è verificata la chiamata a KeSynchronizeExecution (vedere Passaggi 1 e 3).

  • Quando l'ISR di un driver accoda il DpcForIsr, il kernel causa l'esecuzione del DPC sul primo processore disponibile in cui IRQL scende sotto DISPATCH_LEVEL. Questo non è necessariamente lo stesso processore da cui si è verificata la chiamata IoRequestDpc (vedere Passaggio 2).

Le operazioni di I/O guidate da un driver potrebbero essere serializzate in un computer uniprocessore, ma le stesse operazioni possono essere veramente asincrone in un computer SMP. Come illustrato nella figura precedente, l'ISR di un driver potrebbe essere eseguito in CPU4 in un computer SMP prima che il DpcForIsr inizi l'elaborazione di un'IRP per cui l'ISR ha già gestito un interruzione del dispositivo in CPU1.

In altre parole, non è consigliabile presupporre che un blocco di spin di interruzione possa proteggere i dati specifici dell'operazione salvati dall'ISR quando viene sovrascritto da un processore quando si verifica un interruzione del dispositivo in un altro processore prima dell'esecuzione della routine DpcForIsr o CustomDpc .

Anche se un driver potrebbe provare a serializzare tutte le operazioni di I/O basate su interruzioni per conservare i dati raccolti dall'ISR, tale driver non eseguirebbe molto più velocemente in un computer SMP rispetto a un computer uniprocessor. Per ottenere le prestazioni migliori possibili dei driver mentre rimangono portatili tra piattaforme uniprocessor e multiprocessore, i driver devono usare alcune altre tecniche per salvare i dati specifici dell'operazione ottenuti dall'ISR per l'elaborazione successiva da parte del DpcForIsr.

Ad esempio, un ISR può salvare dati specifici dell'operazione nell'IRP che passa al DpcForIsr. Un miglioramento di questa tecnica consiste nell'implementare un DpcForIsr che consulta un conteggio con aumento ISR, elabora il numero di IRP usando i dati forniti da ISR e reimposta il conteggio su zero prima di restituire. Naturalmente, il conteggio deve essere protetto dal blocco di spin di interruzione del driver perché l'ISR e un synchCritSection mantiene il valore in modo dinamico.