Verwendung von Lookaside-Listen

Treiber, die Puffer fester Größe dynamisch zuweisen müssen, um on-demand E/A-Vorgänge durchzuführen, können die Support-Routinen ExXxxLookasideListEx oder ExXxxLookasideList verwenden. Nachdem ein solcher Treiber seine Lookaside-Liste initialisiert hat, hält das Betriebssystem eine gewisse Anzahl von dynamisch zugewiesenen Puffern der gegebenen Größe in der Lookaside-Liste des Treibers vor und reserviert damit effektiv einen Satz von wiederverwendbaren Puffern fester Größe für den Treiber. Das Format und der Inhalt der Puffer fester Größe (auch Einträge genannt) in der Lookaside-Liste eines Treibers werden vom Treiber bestimmt.

So verwenden beispielsweise Speicherklassentreiber, die SCSI-Anforderungsblöcke (SRBs) für die zugrunde liegenden SCSI-Port/Miniport-Treiber einrichten müssen, Lookaside-Listen. Ein solcher Klassentreiber weist Puffer für SRBs nach Bedarf aus seiner Lookaside-Liste zu und veröffentlicht jeden SRB-Puffer zurück in der Lookaside-Liste, damit die Lookaside-Liste ihn wiederverwenden kann, wenn ein SRB in einem abgeschlossenen IRP an den Klassentreiber zurückgegeben wird. Da ein Klassentreiber für Speicher nicht im Voraus bestimmen kann, wie viele SRBs er zu einem bestimmten Zeitpunkt verwenden muss, weil der E/A-Bedarf des Treibers steigt und fällt, ist eine Lookaside-Liste ein bequemer und wirtschaftlicher Weg, um die Zuweisung und Freigabe von Puffern für SRBs fester Größe in einem solchen Treiber zu verwalten.

Das Betriebssystem verwaltet den Status aller aktuell verwendeten Paged- und Non-Paged-Lookaside-Listen und verfolgt dynamisch den Bedarf an Zuweisungen und Freigaben von Einträgen in allen Listen sowie den verfügbaren Pool des Systems für neue Einträge. Wenn die Nachfrage nach Zuweisungen hoch ist, erhöht das Betriebssystem die Anzahl der Einträge, die es in jeder Lookaside-Liste hält. Wenn die Nachfrage wieder sinkt, gibt es überschüssige Lookaside-Einträge wieder an den System-Pool ab.

Lookaside-Listen sind thread-sicher. Eine Lookaside-Liste verfügt über eine integrierte Synchronisierung, die es mehreren, gleichzeitig ausgeführten Threads in einem Treiber ermöglicht, eine Lookaside-Liste gemeinsam zu nutzen. Diese Threads können sicher Puffer aus der gemeinsamen Lookaside-Liste zuweisen und diese Puffer an die Liste zurückgeben, ohne dass der Treiber diese Vorgänge explizit synchronisieren muss. Um jedoch mögliche Lecks und Datenverfälschungen zu vermeiden, muss eine Gruppe von Threads, die sich eine Lookaside-Liste teilen, die Initialisierung und Löschung der Liste explizit synchronisieren.

Schnittstellen für Lookaside-Listen

Ab Windows Vista beschreibt die LOOKASIDE_LIST_EX-Struktur eine Lookaside-Liste, die entweder Paged oder Non-Paged Puffer enthalten kann. Wenn ein Treiber angepasste Allocate und Free-Routinen für diese Lookaside-Liste bereitstellt, erhalten diese Routinen einen privaten Kontext als Input-Parameter. Ein Treiber kann diesen Kontext verwenden, um private Daten für die Lookaside-Liste zu sammeln. Der Kontext kann zum Beispiel dazu verwendet werden, die Anzahl der Listeneinträge zu zählen, die von der Liste dynamisch zugewiesen und freigegeben werden. Ein Code-Beispiel, das zeigt, wie Sie einen Kontext auf diese Weise verwenden können, finden Sie unter ExInitializeLookasideListEx.

Die folgenden vom System bereitgestellten Routinen unterstützen Lookaside-Listen, die durch eine LOOKASIDE_LIST_EX-Struktur beschrieben werden:

ExAllocateFromLookasideListEx

ExDeleteLookasideListEx

ExFlushLookasideListEx

ExFreeToLookasideListEx

ExInitializeLookasideListEx

Beginnend mit Windows 2000 beschreibt die PAGED_LOOKASIDE_LIST-Struktur eine Lookaside-Liste, die Paged-Puffer enthält. Wenn ein Treiber angepasste Allocate- und Free-Routinen für diese Lookaside-Liste bereitstellt, erhalten diese Routinen keinen privaten Kontext als Input-Parameter. Wenn Ihr Treiber nur unter Windows Vista und neueren Versionen von Windows ausgeführt werden soll, sollten Sie aus diesem Grund die LOOKASIDE_LIST_EX-Struktur anstelle der PAGED_LOOKASIDE_LIST-Struktur für Ihre Lookaside-Listen verwenden. Die folgenden vom System bereitgestellten Routinen unterstützen Lookaside-Listen, die durch eine PAGED_LOOKASIDE_LIST-Struktur beschrieben werden:

ExAllocateFromPagedLookasideList

ExDeletePagedLookasideList

ExFreeToPagedLookasideList

ExInitializePagedLookasideList

Beginnend mit Windows 2000 beschreibt die NPAGED_LOOKASIDE_LIST-Struktur eine Lookaside-Liste, die Non-Paged-Puffer enthält. Wenn ein Treiber angepasste Allocate- und Free-Routinen für diese Lookaside-Liste bereitstellt, erhalten diese Routinen keinen privaten Kontext als Input-Parameter. Auch hier gilt: Wenn Ihr Treiber nur unter Windows Vista und späteren Versionen von Windows ausgeführt werden soll, sollten Sie für Ihre Lookaside-Listen die Struktur LOOKASIDE_LIST_EX anstelle der Struktur NPAGED_LOOKASIDE_LIST verwenden. Die folgenden vom System bereitgestellten Routinen unterstützen Lookaside-Listen, die durch eine NPAGED_LOOKASIDE_LIST-Struktur beschrieben werden:

ExAllocateFromNPagedLookasideList

ExDeleteNPagedLookasideList

ExFreeToNPagedLookasideList

ExInitializeNPagedLookasideList

Implementierungsrichtlinien

Um eine Lookaside-Liste zu implementieren, die eine LOOKASIDE_LIST_EX-Struktur verwendet, befolgen Sie diese Design-Richtlinien:

  • Rufen Sie ExInitializeLookasideListEx auf, um eine Lookaside-Liste einzurichten. Geben Sie in diesem Aufruf an, ob die Einträge in der Lookaside-Liste aus Paged- oder Non-Paged-Puffern bestehen sollen. Verwenden Sie Non-Paged-Puffer, wenn der Treiber selbst oder ein darunter liegender Treiber, an den er seine Lookaside-Listeneinträge weitergibt, auf diese Einträge mit IRQL >= DISPATCH_LEVEL zugreifen könnte. Verwenden Sie Paged-Puffer nur, wenn Zugriffe auf die Lookaside-Listeneinträge des Treibers immer mit IRQL <= APC_LEVEL erfolgen.

  • Die LOOKASIDE_LIST_EX-Struktur für die Lookaside-Liste muss sich immer im Non-Paged-Systemspeicher befinden, unabhängig davon, ob die Einträge in der Liste ausgelagert oder nicht ausgelagert sind.

  • Um die Leistung zu verbessern, übergeben Sie NULL-Zeiger für die Allocate- und Free-Parameter an ExInitializeLookasideListEx, es sei denn, die Allokations- und Deallokationsroutinen müssen mehr tun als nur Speicher für Lookaside-Listeneinträge zuzuweisen und kostenlos freizugeben. Beispielsweise könnten diese Routinen Informationen über die Verwendung von dynamisch zugewiesenen Puffern durch den Treiber aufzeichnen.

  • Eine vom Treiber bereitgestellte Allocate-Routine kann die Input-Parameter (PoolType, Tag und Size), die sie erhält, direkt an die ExAllocatePoolWithTag- oder ExAllocatePoolWithQuotaTag-Routine weitergeben, um einen neuen Puffer zuzuweisen.

  • Führen Sie für jeden Aufruf von ExAllocateFromLookasideListEx so schnell wie möglich den reziproken Aufruf von ExFreeToLookasideListEx durch, wenn ein zuvor zugewiesener Eintrag nicht mehr verwendet wird.

Die Bereitstellung von Allocate- und Free-Routinen, die nichts weiter tun, als ExAllocatePoolWithTag bzw. ExFreePool aufzurufen, verschwendet Prozessorzyklen. ExAllocateFromLookasideListEx führt die erforderlichen Aufrufe an ExAllocatePoolWithTag und ExFreePool automatisch aus, wenn ein Treiber NULL Allocate und Free Zeiger an ExInitializeLookasideListEx übergibt.

Eine vom Treiber bereitgestellte Allocate-Routine darf keinen Speicher für einen Eintrag aus einem Paged-Pool allozieren, der in einer Non-Paged-Lookaside-Liste gehalten werden soll oder andersherum. Sie muss außerdem Einträge mit fester Größe zuweisen, da jeder nachfolgende Treiberaufruf ExAllocateFromLookasideListEx den ersten Eintrag zurückgibt, der aktuell in der Lookaside-Liste gehalten wird, es sei denn, die Liste ist leer. Das heißt, ein Aufruf von ExAllocateFromLookasideListEx bewirkt nur dann einen Aufruf der vom Treiber bereitgestellten Allocate-Routine, wenn die angegebene Lookaside-Liste derzeit leer ist. Daher wird bei jedem Aufruf von ExAllocateFromLookasideListEx der zurückgegebene Eintrag nur dann genau die Größe haben, die der Treiber benötigt, wenn alle Einträge in der Lookaside-Liste eine feste Größe haben. Eine vom Treiber bereitgestellte Allocate-Routine sollte auch nicht den Tag-Wert ändern, den der Treiber ursprünglich an ExInitializeLookasideListEx übergeben hat, da Änderungen des Pool-Tag-Wertes die Fehlersuche und die Nachverfolgung der Speichernutzung des Treibers erschweren würden.

Aufrufe von ExFreeToLookasideListEx speichern zuvor zugewiesene Einträge in der Lookaside-Liste, es sei denn, die Liste ist bereits voll (d. h. die Liste enthält die vom System festgelegte maximale Anzahl von Einträgen). Um die Leistung zu verbessern, sollte ein Treiber für jeden Aufruf von ExAllocateFromLookasideListEx so schnell wie möglich einen reziproken Aufruf von ExFreeToLookasideListEx durchführen. Wenn ein Treiber Einträge schnell wieder für seine Lookaside-Liste freigibt, ist es weit weniger wahrscheinlich, dass der nächste Aufruf dieses Treibers an ExAllocateFromLookasideListEx den Leistungsnachteil einer dynamischen Speicherzuweisung für einen neuen Eintrag mit sich bringt.

Ähnliche Richtlinien gelten für eine Lookaside-Liste, die eine PAGED_LOOKASIDE_LIST- oder NPAGED_LOOKASIDE_LIST-Struktur verwendet.