Freigeben über


Rundown-Schutz

Ab Windows XP ist der Rundown-Schutz für Treiber im Kernel-Modus verfügbar. Treiber können den Rundown-Schutz verwenden, um sicher auf Objekte im gemeinsamen Systemspeicher zuzugreifen, die von einem anderen Treiber im Kernel-Modus erstellt und gelöscht werden.

Ein Objekt wird als Rundown bezeichnet, wenn alle ausstehenden Zugriffe auf das Objekt abgeschlossen sind und keine neuen Anfragen für den Zugriff auf das Objekt gewährt werden. Ein gemeinsam genutztes Objekt muss zum Beispiel als Rundown behandelt werden, damit es gelöscht und durch ein neues Objekt ersetzt werden kann.

Der Treiber, dem das gemeinsam genutzte Objekt gehört, kann anderen Treibern die Möglichkeit geben, den Rundown-Schutz für das Objekt zu übernehmen und aufzuheben. Wenn der Rundown-Schutz in Kraft ist, kann ein anderer Treiber als der Besitzer auf das Objekt zugreifen, ohne dass die Gefahr besteht, dass der Besitzer das Objekt löscht, bevor der Zugriff abgeschlossen ist. Bevor der Zugriff beginnt, fordert der zugreifende Treiber den Rundown-Schutz für das Objekt an. Bei einem langlebigen Objekt wird dieser Anfrage fast immer stattgegeben. Nachdem der Zugriff abgeschlossen ist, gibt der zugreifende Treiber seinen zuvor erworbenen Rundown-Schutz für das Objekt frei.

Primäre Rundown-Schutz-Funktionen

Um mit der gemeinsamen Nutzung eines Objekts zu beginnen, ruft der Treiber, dem das Objekt gehört, die Funktion ExInitializeRundownProtection auf, um den Rundown-Schutz für das Objekt zu initialisieren. Nach diesem Aufruf können andere Treiber, die auf das Objekt zugreifen, den Rundown-Schutz für das Objekt übernehmen und aufheben.

Ein Treiber, der auf das freigegebene Objekt zugreift, ruft die Funktion ExAcquireRundownProtection auf, um einen Rundown-Schutz für das Objekt anzufordern. Nachdem der Zugriff abgeschlossen ist, ruft dieser Treiber die Funktion ExReleaseRundownProtection auf, um den Rundown-Schutz für das Objekt aufzuheben.

Wenn der besitzende Treiber feststellt, dass das gemeinsam genutzte Objekt gelöscht werden muss, wartet dieser Treiber mit dem Löschen des Objekts, bis alle ausstehenden Zugriffe auf das Objekt abgeschlossen sind.

In Vorbereitung auf das Löschen des gemeinsam genutzten Objekts ruft der besitzende Treiber die Funktion ExWaitForRundownProtectionRelease auf, um darauf zu warten, dass das Objekt ausgeführt wird. Während dieses Aufrufs wartet ExWaitForRundownProtectionRelease darauf, dass alle zuvor für das Objekt ausgeführten Instanzen des Rundown-Schutzes freigegeben werden, verhindert aber, dass neue Anfragen für den Rundown-Schutz des Objekts gewährt werden. Nachdem der letzte geschützte Zugriff abgeschlossen ist und alle Instanzen des Rundown-Schutzes freigegeben sind, kehrt ExWaitForRundownProtectionRelease zurück und der besitzende Treiber kann das Objekt sicher löschen.

ExWaitForRundownProtectionRelease blockiert die Ausführung des aufrufenden Treiber-Threads, bis alle Treiber, die den Rundown-Schutz für das gemeinsame Objekt ausführen, diesen Schutz aufheben. Um zu verhindern, dass ExWaitForRundownProtectionRelease die Ausführung übermäßig lange blockiert, sollten Treiber-Threads, die auf das gemeinsam genutzte Objekt zugreifen, nicht ausgesetzt werden, während sie den Rundown-Schutz für das Objekt halten. Aus diesem Grund sollten Treiber, die auf das Objekt zugreifen, ExAcquireRundownProtection und ExReleaseRundownProtection innerhalb einer kritischen Region oder einer geschützten Region aufrufen oder während sie mit IRQL = APC_LEVEL ausgeführt werden.

Verwendungsmöglichkeiten für Rundown-Schutz

Der Rundown-Schutz ist besonders nützlich, um den Zugriff auf ein gemeinsam genutztes Objekt zu ermöglichen, das fast immer verfügbar ist, aber möglicherweise gelegentlich gelöscht und ersetzt werden muss. Treiber, die auf Daten zugreifen oder Funktionen in diesem Objekt aufrufen, dürfen nicht versuchen, auf das Objekt zuzugreifen, nachdem es gelöscht wurde. Andernfalls könnten diese ungültigen Zugriffe zu unvorhersehbarem Verhalten, Datenbeschädigung oder sogar zu Systemausfällen führen.

Ein Antivirentreiber beispielsweise bleibt normalerweise im Speicher geladen, wenn das Betriebssystem ausgeführt wird. Gelegentlich muss dieser Treiber möglicherweise entladen und durch eine aktualisierte Version des Treibers ersetzt werden. Andere Treiber senden E/A-Anfragen an den Antivirentreiber, um auf die Daten und Funktionen dieses Treibers zuzugreifen. Bevor eine E/A-Anforderung gesendet wird, kann eine Kernel-Komponente, z. B. ein Dateisystem-Filter-Manager, einen Rundown-Schutz aktivieren, um zu verhindern, dass der Antivirentreiber vorzeitig entladen wird, während er die E/A-Anforderung bearbeitet. Nachdem die E/A-Anforderung ausgeführt wurde, kann der Rundown-Schutz wieder aufgehoben werden.

Mit dem Rundown-Schutz werden Zugriffe auf ein gemeinsames Objekt nicht serialisiert. Wenn zwei oder mehr zugreifende Treiber gleichzeitig einen Rundown-Schutz auf ein Objekt ausüben können und Zugriffe auf das Objekt serialisiert werden müssen, muss ein anderer Mechanismus, wie z. B. ein Mutual-Exclusion Lock, verwendet werden, um die Zugriffe zu serialisieren.

Die Struktur EX_RUNDOWN_REF

Eine EX_RUNDOWN_REF-Struktur verfolgt den Status des Rundown-Schutzes für ein gemeinsam genutztes Objekt. Diese Struktur ist für Treiber nicht sichtbar. Die vom System bereitgestellten Rundown-Schutz-Funktionen verwenden diese Struktur, um die Anzahl der Instanzen des Rundown-Schutzes zu zählen, die derzeit für ein Objekt wirksam sind. Diese Funktionen verwenden diese Struktur außerdem, um festzustellen, ob das Objekt bereits als Rundown gekennzeichnet ist oder gerade als Rundown gekennzeichnet werden soll.

Um mit der Freigabe eines Objekts zu beginnen, ruft der Treiber, dem das Objekt gehört, ExInitializeRundownProtection auf, um die mit dem Objekt verbundene EX_RUNDOWN_REF-Struktur zu initialisieren. Nach der Initialisierung kann der besitzende Treiber diese Struktur anderen Treibern, die Zugriff auf das Objekt benötigen, zur Verfügung stellen. Die zugreifenden Treiber übergeben diese Struktur als Parameter an die ExAcquireRundownProtection- und ExReleaseRundownProtection-Aufrufe, die den Rundown-Schutz für das Objekt übernehmen und freigeben. Der besitzende Treiber übergibt diese Struktur als Parameter an den Aufruf von ExWaitForRundownProtectionRelease, der darauf wartet, dass das Objekt als Rundown gekennzeichnet wird, damit es sicher gelöscht werden kann.

Vergleich mit Sperren

Rundown-Schutz ist eine von mehreren Möglichkeiten, den sicheren Zugriff auf ein gemeinsam genutztes Objekt zu gewährleisten. Ein anderer Ansatz ist die Verwendung eines Mutual-Exclusion Software-Locks. Wenn ein Treiber Zugriff auf ein Objekt benötigt, das derzeit von einem anderen Treiber gesperrt ist, muss der erste Treiber warten, bis der zweite Treiber die Sperre freigibt. Das Übernehmen und Freigeben von Sperren kann jedoch zu einem Leistungsengpass werden, und Sperren können große Mengen an Speicher verbrauchen. Bei unsachgemäßer Verwendung können Sperren dazu führen, dass Treiber, die um dieselben gemeinsamen Objekte konkurrieren, in ein Deadlock geraten. Die Bemühungen, Deadlocks zu erkennen und zu vermeiden, erfordern in der Regel die Nutzung beträchtlicher Datenverarbeitungsressourcen.

Im Gegensatz zu Sperren benötigt der Rundown-Schutz relativ wenig Verarbeitungszeit und Speicherplatz. Ein einfacher Referenzzähler wird mit dem Objekt verknüpft, um sicherzustellen, dass das Löschen des Objekts aufgeschoben wird, bis alle ausstehenden Zugriffe auf das Objekt abgeschlossen sind. Mit diesem Ansatz können atomare Interlock-Hardwarebefehle anstelle von Mutual-Exclusion Software-Locks verwendet werden, um einen sicheren Zugriff auf ein Objekt zu gewährleisten. Die Aufrufe zur Übernahme und Freigabe des Rundown-Schutzes sind in der Regel sehr schnell. Die Vorteile eines einfach zu handhabenden Mechanismus wie des Rundown-Schutzes können bei einem gemeinsam genutzten Objekt, das eine lange Lebensdauer hat und von vielen Treibern gemeinsam genutzt wird, erheblich sein.

Andere Rundown-Schutz-Funktionen

Zusätzlich zu den bereits erwähnten Funktionen gibt es noch mehrere andere Rundown-Schutz-Funktionen. Diese zusätzlichen Funktionen werden möglicherweise von einigen Treibern verwendet.

Die Funktion ExReInitializeRundownProtection ermöglicht es, eine zuvor verwendete EX_RUNDOWN_REF-Struktur mit einem neuen Objekt zu verknüpfen und den Rundown-Schutz für dieses Objekt zu initialisieren.

Die Funktion ExRundownCompleted aktualisiert die Struktur EX_RUNDOWN_REF, um anzuzeigen, dass der Rundown des zugehörigen Objekts abgeschlossen ist.

Die Funktionen ExAcquireRundownProtectionEx und ExReleaseRundownProtectionEx entsprechen den Funktionen ExAcquireRundownProtection und ExReleaseRundownProtection. Diese vier Funktionen erhöhen oder verringern die Anzahl der Instanzen des Rundown-Schutzes, die auf ein gemeinsames Objekt wirken. Während ExAcquireRundownProtection und ExReleaseRundownProtection diese Zählung um eins erhöhen und verringern, erhöhen und verringern ExAcquireRundownProtectionEx und ExReleaseRundownProtectionEx die Zählung um beliebige Werte.

Rundown-Schutz mit Cache-Unterstützung

Eine Rundown-Referenz ist eine kompakte und schnelle Datenstruktur, aber sie kann zu Cache-Konflikten führen, wenn viele Prozesse versuchen, die Referenz zur gleichen Zeit zu übernehmen. Dies kann die Leistung und Skalierbarkeit Ihres Treibers beeinträchtigen.

Um dieses Problem zu vermeiden, können Sie eine Cache-fähige Rundown-Referenz verwenden, um die Referenzverfolgung auf mehrere Cache-Zeilen zu verteilen. Dadurch wird die Cache-Belastung reduziert und die Leistung Ihres Treibers auf Multiprozessor-Computern verbessert.

Um eine Cache-fähige Rundown-Referenz zu verwenden, gehen Sie wie folgt vor:

  1. Erstellen Sie ein EX_RUNDOWN_REF_CACHE_AWARE-Objekt, indem Sie einen der folgenden Schritte ausführen:
  2. Fordern Sie den Rundown-Schutz für das Objekt an, bevor Sie darauf zugreifen, indem Sie die Funktion ExAcquireRundownProtectionCacheAware aufrufen. Diese Funktion gibt WAHR zurück, wenn der Anfrage stattgegeben wird, oder FALSCH, wenn das Objekt im Rundown ist.
  3. Geben Sie den Rundown-Schutz des Objekts nach dem Zugriff auf das Objekt frei, indem Sie die Funktion ExReleaseRundownProtectionCacheAware aufrufen.
  4. Warten Sie, bis der Rundown des Objekts abgelaufen ist, bevor Sie es löschen, indem Sie die Funktion ExWaitForRundownProtectionReleaseCacheAware aufrufen. Diese Funktion blockiert den aktuellen Thread, bis alle Instanzen des Rundown-Schutzes für das Objekt freigegeben sind.
  5. Wenn der Treiber vorher ExAllocateCacheAwareRundownProtection aufgerufen hat, sollte er ExFreeCacheAwareRundownProtection aufrufen, um die Rundown-Referenz freizugeben.

Um eine Cache-fähige Rundown-Referenz wiederzuverwenden, gehen Sie wie folgt vor:

  1. Nach dem Aufruf von ExWaitForRundownProtectionReleaseCacheAware rufen Sie ExRundownCompletedCacheAware auf, um anzuzeigen, dass der Rundown des alten Objekts abgeschlossen ist.
  2. Rufen Sie ExReInitializeRundownProtectionCacheAware auf, um die Referenz neu zu initialisieren, nachdem der Rundown des zugehörige Objekts erledigt ist.
  3. Jetzt kann der Treiber erneut ExAcquireRundownProtectionCacheAware aufrufen.

Eine Cache-fähige Rundown-Referenz hat den Vorteil einer besseren Leistung und Skalierbarkeit in bestimmten Situationen, verbraucht aber mehr Speicher als eine normale Rundown-Referenz. Sie sollten diese Möglichkeit in Betracht ziehen, wenn Sie zwischen den beiden Arten von Rundown-Referenzen wählen.