Freigeben über


Verwenden von Spinsperren: Ein Beispiel

Die Minimierung der Zeit, die ein Treiber für Drehsperren hält, kann sowohl die Leistung des Treibers als auch des Systems insgesamt erheblich verbessern. Betrachten Sie beispielsweise die folgende Abbildung, die zeigt, wie eine Interrupt-Spinsperre gerätespezifische Daten schützt, die zwischen einem ISR und den StartIo - und DpcForIsr-Routinen auf einem SMP-Computer freigegeben werden müssen.

Diagramm, das die Verwendung einer Unterbrechungsdrehsperre veranschaulicht.

  1. Während der ISR des Treibers bei DIRQL auf einem Prozessor ausgeführt wird, wird die StartIo-Routine bei DISPATCH_LEVEL auf einem zweiten Prozessor ausgeführt. Der Kernel-Interrupthandler enthält interruptSpinLock für den ISR des Treibers, der auf geschützte, gerätespezifische Daten wie Zustand oder Zeiger auf Geräteregister (SynchronizeContext) in der Geräteerweiterung des Treibers zugreift. Die StartIo-Routine , die für den Zugriff auf SynchronizeContext bereit ist, ruft KeSynchronizeExecution auf und übergibt einen Zeiger auf die zugeordneten Interrupt-Objekte, den freigegebenen SynchronizeContext und die SynchCritSection-Routine des Treibers (AccessDevice in der vorherigen Abbildung).

    Bis der ISR zurückgibt und dadurch interruptSpinLock des Treibers loslässt, dreht sich KeSynchronizeExecution auf dem zweiten Prozessor, hindert AccessDevice daran, SynchronizeContext zu berühren. KeSynchronizeExecution löst jedoch auch IRQL für den zweiten Prozessor auf die SynchronizeIrql der Interruptobjekte aus, wodurch verhindert wird, dass ein weiterer Geräteunterbrechung auf diesem Prozessor auftritt, sodass AccessDevice bei DIRQL ausgeführt werden kann, sobald der ISR zurückgibt. Höhere DIRQL-Interrupts für andere Geräte, Taktunterbrechungen und Power-Fail-Interrupts können jedoch weiterhin auf beiden Prozessoren auftreten.

  2. Wenn der ISR den DpcForIsr des Treibers in die Warteschlange stellt und zurückgibt, wird AccessDevice auf dem zweiten Prozessor an der SynchronizeIrql der zugehörigen Interruptobjekte ausgeführt und greift auf SynchronizeContext zu. In der Zwischenzeit wird der DpcForIsr auf einem anderen Prozessor bei DISPATCH_LEVEL IRQL ausgeführt. DpcForIsr ist auch bereit für den Zugriff auf SynchronizeContext, sodass KeSynchronizeExecution mit den gleichen Parametern aufgerufen wird, die die StartIo-Routine in Schritt 1 ausgeführt hat.

    Wenn KeSynchronizeExecution die Spin-Sperre erwirbt und AccessDevice im Auftrag der StartIo-Routine ausführt, erhält die vom Treiber bereitgestellte Synchronisierungsroutine AccessDevice exklusiven Zugriff auf SynchronizeContext. Da AccessDevice auf dem SynchronizeIrql ausgeführt wird, kann der ISR des Treibers die Spinsperre nicht abrufen und auf denselben Bereich zugreifen, bis die Spinsperre aufgehoben wird, auch wenn das Gerät während der Ausführung von AccessDevice auf einem anderen Prozessor unterbricht.

  3. AccessDevice gibt zurück und gibt die Drehsperre auf. Die StartIo-Routine wird bei DISPATCH_LEVEL auf dem zweiten Prozessor fortgesetzt. KeSynchronizeExecution führt jetzt AccessDevice auf dem dritten Prozessor aus, sodass er im Auftrag von DpcForIsr auf SynchronizeContext zugreifen kann. Wenn jedoch vor dem DpcForIsr in Schritt 2 ein Geräteunterbrechung namens KeSynchronizeExecution aufgetreten ist, kann der ISR auf einem anderen Prozessor ausgeführt werden, bevor KeSynchronizeExecution die Spinsperre abrufen und AccessDevice auf dem dritten Prozessor ausführen konnte.

Wie die vorherige Abbildung zeigt, enthält eine Routine, die auf einem Prozessor ausgeführt wird, eine Drehsperre, aber jede andere Routine, die versucht, diese Spinsperre zu erwerben, keine Arbeit. Jede Routine, die versucht, eine bereits gehaltene Spinsperre zu erhalten, wird einfach auf dem aktuellen Prozessor gedreht, bis der Halter die Spinsperre freigibt. Wenn eine Spinsperre aufgehoben wird, kann sie nur von einer Routine abgerufen werden. jede andere Routine, die derzeit versucht, die gleiche Drehsperre zu erhalten, wird weiterhin gedreht.

Der Halter einer beliebigen Drehsperre wird bei einem erhöhten IRQL ausgeführt: entweder bei DISPATCH_LEVEL für eine Executive Spin-Sperre oder bei einer DIRQL für eine Unterbrechungs-Drehsperre. Aufrufer von KeAcquireSpinLock und KeAcquireInStackQueuedSpinLock werden bei DISPATCH_LEVEL ausgeführt, bis sie KeReleaseSpinLock oder KeReleaseInStackQueuedSpinLock aufrufen, um die Sperre zu lösen. Aufrufer von KeSynchronizeExecution heben IRQL für den aktuellen Prozessor automatisch auf die SynchronizeIrql der Interruptobjekte aus, bis die vom Aufrufer bereitgestellte SynchCritSection-Routine beendet und KeSynchronizeExecution die Steuerung zurückgibt. Weitere Informationen finden Sie unter Aufrufen von Supportroutinen, die Drehsperren verwenden.

Beachten Sie die folgende Tatsache über die Verwendung von Drehsperren:

Bei allen Code, der mit einem niedrigeren IRQL ausgeführt wird, kann keine Arbeit für den Satz von Prozessoren ausgeführt werden, die von einem Spin-Lock-Halter und anderen Routinen, die versuchen, dieselbe Spinsperre zu erhalten, belegt sind.

Folglich führt die Minimierung der Zeit, die ein Treiber für Drehsperren hält, zu einer deutlich besseren Treiberleistung und trägt erheblich zu einer besseren Gesamtleistung des Systems bei.

Wie die vorherige Abbildung zeigt, führt der Kernel-Interrupthandler Routinen aus, die auf demselben IRQL-Computer auf einem Multiprozessorcomputer ausgeführt werden. Der Kernel führt außerdem Folgendes aus:

  • Wenn eine Treiberroutine KeSynchronizeExecution aufruft, bewirkt der Kernel, dass die SynchCritSection-Routine des Treibers auf demselben Prozessor ausgeführt wird, von dem aus der Aufruf von KeSynchronizeExecution erfolgt ist (siehe Schritte 1 und 3).

  • Wenn der ISR eines Treibers seine DpcForIsr in die Warteschlange stellt, bewirkt der Kernel, dass der DPC auf dem ersten verfügbaren Prozessor ausgeführt wird, für den IRQL unter DISPATCH_LEVEL fällt. Dies ist nicht unbedingt derselbe Prozessor, von dem aus der IoRequestDpc-Aufruf erfolgt ist (siehe Schritt 2).

Die unterbrechungsgesteuerten E/A-Vorgänge eines Treibers können in der Regel auf einem Uniprozessorcomputer serialisiert werden, aber dieselben Vorgänge können auf einem SMP-Computer wirklich asynchron sein. Wie die vorherige Abbildung zeigt, kann der ISR eines Treibers auf CPU4 auf einem SMP-Computer ausgeführt werden, bevor sein DpcForIsr mit der Verarbeitung eines IRP beginnt, für den der ISR bereits einen Geräteunterbrechung auf CPU1 verarbeitet hat.

Anders ausgedrückt: Sie sollten nicht davon ausgehen, dass eine Unterbrechungs-Spinsperre vorgangsspezifische Daten schützen kann, die der ISR speichert, wenn sie auf einem Prozessor ausgeführt werden, bevor die DpcForIsr - oder CustomDpc-Routine ausgeführt wird.

Obwohl ein Treiber versuchen könnte, alle unterbrechungsgesteuerten E/A-Vorgänge zu serialisieren, um die vom ISR gesammelten Daten beizubehalten, würde dieser Treiber auf einem SMP-Computer nicht viel schneller ausgeführt als auf einem Uniprozessorcomputer. Um die bestmögliche Treiberleistung zu erzielen und gleichzeitig über Uniprozessor- und Multiprozessorplattformen portierbar zu bleiben, sollten Treiber eine andere Technik verwenden, um vorgangsspezifische Daten, die vom ISR erhalten wurden, für die nachfolgende Verarbeitung durch den DpcForIsr zu speichern.

Beispielsweise kann ein ISR vorgangsspezifische Daten in der IRP speichern, die an den DpcForIsr übergeben wird. Eine Verfeinerung dieser Technik besteht darin, einen DpcForIsr zu implementieren, der eine durch ISR erweiterte Anzahl abhört, die Anzahl der IRPs der Anzahl mithilfe von ISR bereitgestellten Daten verarbeitet und die Anzahl vor der Rückgabe auf 0 zurücksetzt. Natürlich muss die Anzahl durch die Interrupt-Spinsperre des Treibers geschützt werden, da dessen ISR und eine SynchCritSection ihren Wert dynamisch beibehalten würden.