Verwenden von Lookaside-Listen

Treiber, die Puffer mit fester Größe dynamisch zuordnen müssen, um bedarfsgesteuerte E/A-Vorgänge auszuführen, können die Supportroutinen ExXxxLookasideListEx oder ExXxxLookasideList verwenden. Nachdem ein solcher Treiber seine Lookaside-Liste initialisiert hat, enthält das Betriebssystem einige dynamisch zugeordnete Puffer der angegebenen Größe in der Lookaside-Liste des Treibers, wodurch eine Reihe wiederverwendbarer Puffer mit fester Größe für den Treiber reserviert wird. Format und Inhalt der Puffer mit fester Größe eines Treibers (auch als Einträge bezeichnet) in seiner Lookaside-Liste werden vom Treiber bestimmt.

Beispielsweise verwenden Speicherklassentreiber, die SCSI-Anforderungsblöcke (SRBs) für die zugrunde liegenden SCSI-Port-/Miniporttreiber einrichten müssen, Lookaside-Listen. Ein solcher Klassentreiber ordnet Puffer für SRBs nach Bedarf aus seiner Lookaside-Liste zu und gibt jeden SRB-Puffer für die Lookaside-Liste wieder frei, um sie wiederzuverwenden, wenn ein SRB an den Klassentreiber in einem abgeschlossenen IRP zurückgegeben wird. Da ein Speicherklassentreiber nicht vordefinieren kann, wie viele SRBs er jederzeit verwenden muss, da die E/A-Nachfrage für den Treiber steigt und sinkt, ist eine Lookaside-Liste eine bequeme und kostengünstige Möglichkeit, die Zuordnung und Aufhebung der Puffer für SRBs mit fester Größe in einem solchen Treiber zu verwalten.

Das Betriebssystem verwaltet den Zustand aller ausgelagerten und nicht ausgelagerten Lookaside-Listen, die derzeit verwendet werden, und verfolgt dynamisch die Nachfrage nach Zuordnungen und Zuordnungen von Einträgen in allen Listen sowie den verfügbaren Systempool für neue Einträge. Wenn die Nachfrage nach Zuordnungen hoch ist, erhöht das Betriebssystem die Anzahl der Einträge, die es in jeder Suchliste enthält. Wenn die Nachfrage wieder sinkt, werden Überschuss-Lookaside-Einträge wieder in den Systempool frei.

Lookaside-Listen sind threadsicher. Eine Lookaside-Liste verfügt über eine integrierte Synchronisierung, um mehrere gleichzeitig ausgeführte Threads in einem Treiber zum Freigeben einer Suchliste zu ermöglichen. Diese Threads können Puffer aus der freigegebenen Lookaside-Liste sicher zuordnen und diese Puffer für die Liste freigeben, ohne dass der Treiber diese Vorgänge explizit synchronisieren muss. Um jedoch mögliche Datenlecks und Datenbeschädigungen zu vermeiden, muss eine Reihe von Threads, die eine Lookaside-Liste gemeinsam nutzen, die Initialisierung und Löschung der Liste explizit synchronisieren.

Lookaside-Listenschnittstellen

Ab Windows Vista beschreibt die LOOKASIDE_LIST_EX-Struktur eine Lookaside-Liste, die ausgelagerte oder nicht ausgelagerte Puffer enthalten kann. Wenn ein Treiber benutzerdefinierte Allocate - und Free-Routinen für diese Lookaside-Liste bereitstellt, erhalten diese Routinen einen privaten Kontext als Eingabeparameter. Ein Treiber kann diesen Kontext verwenden, um private Daten für die Suchliste zu sammeln. Beispielsweise kann der Kontext verwendet werden, um die Anzahl der Listeneinträge zu zählen, die dynamisch zugeordnet und von der Liste freigegeben werden. Ein Codebeispiel, das zeigt, wie ein Kontext auf diese Weise verwendet wird, finden Sie unter ExInitializeLookasideListEx.

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

ExAllocateFromLookasideListEx

ExDeleteLookasideListEx

ExFlushLookasideListEx

ExFreeToLookasideListEx

ExInitializeLookasideListEx

Ab Windows 2000 beschreibt die PAGED_LOOKASIDE_LIST-Struktur eine Lookaside-Liste, die ausgelagerte Puffer enthält. Wenn ein Treiber benutzerdefinierte Allocate - und Free-Routinen für diese Suchliste bereitstellt, erhalten diese Routinen keinen privaten Kontext als Eingabeparameter. Wenn Ihr Treiber nur unter Windows Vista und höheren Versionen von Windows ausgeführt werden soll, sollten Sie daher 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

Ab Windows 2000 beschreibt die NPAGED_LOOKASIDE_LIST-Struktur eine Suchliste, die nicht auslagerte Puffer enthält. Wenn ein Treiber benutzerdefinierte Allocate - und Free-Routinen für diese Suchliste bereitstellt, erhalten diese Routinen keinen privaten Kontext als Eingabeparameter. Wenn Ihr Treiber nur unter Windows Vista und höheren Versionen von Windows ausgeführt werden soll, sollten Sie die LOOKASIDE_LIST_EX-Struktur anstelle der NPAGED_LOOKASIDE_LIST-Struktur für Ihre Lookaside-Listen 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

Befolgen Sie die folgenden Entwurfsrichtlinien, um eine Lookaside-Liste zu implementieren, die eine LOOKASIDE_LIST_EX-Struktur verwendet:

  • Rufen Sie ExInitializeLookasideListEx auf, um eine Suchliste einzurichten. Geben Sie in diesem Aufruf an, ob die Einträge in der Lookaside-Liste ausgelagert oder nicht ausgelagert werden sollen. Verwenden Sie nicht auslagerte Puffer, wenn der Treiber selbst oder ein zugrunde liegender Treiber, an den er seine Suchlisteneinträge übergibt, unter IRQL >= DISPATCH_LEVEL auf diese Einträge zugreifen kann. Verwenden Sie ausgelagerte Puffer nur, wenn Zugriffe auf die Lookaside-Listeneinträge des Treibers immer am IRQL <= APC_LEVEL erfolgen.

  • Die LOOKASIDE_LIST_EX-Struktur für die Suchliste muss sich immer im nicht ausgelagerten 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 Parameter Allocate und Free an ExInitializeLookasideListEx , es sei denn, die Zuordnungs- und Aufhebungsroutinen müssen mehr tun, als nur Arbeitsspeicher für Suchlisteneinträge zuzuweisen und freizugeben. Beispielsweise können diese Routinen Informationen zur Verwendung dynamisch zugeordneter Puffer durch den Treiber aufzeichnen.

  • Eine vom Treiber bereitgestellte Allocate-Routine kann die Eingabeparameter (PoolType, Tag und Size) übergeben, die sie direkt an die ExAllocatePoolWithTag - oder ExAllocatePoolWithQuotaTag-Routine empfängt, um einen neuen Puffer zuzuweisen.

  • Führen Sie für jeden Aufruf von ExAllocateFromLookasideListEx den wechselseitigen Aufruf von ExFreeToLookasideListEx so bald wie möglich aus, wenn ein zuvor zugeordneter Eintrag nicht mehr verwendet wird.

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

Jede vom Treiber bereitgestellte Zuordnungsroutine darf keinen Speicher für einen Eintrag aus einem ausgelagerten Pool zuweisen, der in einer nicht ausgelagerten Lookaside-Liste gespeichert wird oder umgekehrt. Außerdem müssen Einträge mit fester Größe zugeordnet werden, da jeder nachfolgende Treiberaufruf von ExAllocateFromLookasideListEx den ersten Eintrag zurückgibt, der derzeit in der Suchliste enthalten ist, es sei denn, die Liste ist leer. Das heißt, ein Aufruf von ExAllocateFromLookasideListEx führt nur dann zu einem Aufruf der vom Treiber bereitgestellten Allocate-Routine , wenn die angegebene Lookaside-Liste derzeit leer ist. Daher hat der zurückgegebene Eintrag bei jedem Aufruf von ExAllocateFromLookasideListEx genau die Größe, die der Treiber nur benötigt, wenn alle Einträge in der Suchliste eine feste Größe aufweisen. Eine vom Treiber bereitgestellte Allocate-Routine sollte auch den Tag-Wert nicht ändern, den der Treiber ursprünglich an ExInitializeLookasideListEx übergeben hat, da Änderungen im Wert des Pooltags das Debuggen und die Nachverfolgung der Speicherauslastung des Treibers erschweren würden.

Aufrufe von ExFreeToLookasideListEx speichern zuvor zugeordnete Einträge in der Lookaside-Liste, es sei denn, die Liste ist bereits voll (das heißt, die Liste enthält die vom System festgelegte maximale Anzahl von Einträgen). Um die Leistung zu verbessern, sollte ein Treiber exFreeToLookasideListEx so schnell wie möglich für jeden Aufruf von ExAllocateFromLookasideListEx aufrufen. Wenn ein Treiber Einträge schnell wieder in seine Lookaside-Liste freigibt, ist der nächste Aufruf dieses Treibers bei ExAllocateFromLookasideListEx weitaus weniger wahrscheinlich, dass die Leistungseinbußen durch die dynamische Zuweisung von Arbeitsspeicher für einen neuen Eintrag entstehen.

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