ルックアサイド リストの使用

オンデマンド I/O 操作を実行するために、固定サイズバッファーを動的に割り当てる必要があるドライバーは、ExXxxLookasideListEx または ExXxxLookasideList サポート ルーチンを使用できます。 このようなドライバーがルックアサイド リストを初期化した後、オペレーティング システムは、ドライバーのルックアサイド リスト内の特定のサイズの動的に割り当てられたバッファーの数を保持し、ドライバー用に再利用可能な固定サイズのバッファのセットを効果的に予約します。 ルックアサイド リスト内のドライバーの固定サイズ バッファー (エントリとも呼ばれる) の形式と内容は、ドライバーによって決定されます。

たとえば、基になる SCSI ポート/ミニポート ドライバーの SCSI 要求ブロック (SRB) を設定する必要がある記憶域クラス ドライバーは、ルックアサイド リストを使用します。 このようなクラス ドライバーは、ルックアサイド リストから必要に応じて SRB のバッファーを割り当て、完了した IRP のクラス ドライバーに SRB が返されるたびに、各 SRB バッファーをルックアサイド リストに解放し、ルックアサイド リストが再利用できるようにします。 なぜなら記憶域クラス ドライバーでは、ドライバーの I/O 要求が増加および減少するので、使用する必要がある SLB の数をいつでも事前に決定できないためです。ルックアサイド リストは、このようなドライバーの固定サイズ SRB のバッファーの割り当てと割り当て解除を管理するための便利で経済的な方法です。

オペレーティング システムメインは、現在使用されているすべてのページおよび非ページ ルックアサイド リストに関する状態を保持します。また、すべてのリスト内のエントリの割り当てと割り当て解除の要求、および新しいエントリに対して使用可能なシステム プールを動的に追跡します。 割り当ての需要が高い場合、オペレーティング システムは、各ルックアサイド リストに保持されているエントリの数を増やします。 需要が再び低下すると、余分なルックアサイド エントリがシステム プールに戻されます。

ルックアサイド リストはスレッド セーフです。 ルックアサイド リストには、同期が組み込まれており、ドライバーで同時に実行される複数のスレッドがルックアサイド リストを共有することができます。 これらのスレッドは、共有ルックアサイド リストからバッファーを安全に割り当てることができ、ドライバーがこれらの操作を明示的に同期しなくても、これらのバッファーを一覧に解放できます。 ただし、リークやデータ破損を回避するには、ルックアサイド リストを共有するスレッドのセットで、リストの初期化と削除を明示的に同期する必要があります。

ルックアサイド リスト インターフェイス

Windows Vista 以降では、 LOOKASIDE_LIST_EX 構造体は、ページ バッファーまたは非ページ バッファーを含むことができるルックアサイド リストを記述します。 ドライバーは、このルックアサイド リストのカスタム割り当て解放ルーチンを提供する場合、これらのルーチンは、入力パラメーターとしてプライベート コンテキストを受け取ります。 ドライバーは、このコンテキストを使用して、ルックアサイド リストのプライベート データを収集できます。 たとえば、コンテキストを使用して、リストによって動的に割り当てられ、解放されるリスト エントリの数をカウントできます。 この方法でコンテキストを使用する方法を示すコード例については、「ExInitializeLookasideListEx」を参照してください。

次のシステム提供ルーチンは、LOOKASIDE_LIST_EX 構造体によって記述されるルックアサイド リストをサポートします。

ExAllocateFromLookasideListEx

ExDeleteLookasideListEx

ExFlushLookasideListEx

ExFreeToLookasideListEx

ExInitializeLookasideListEx

Windows 2000 以降、 PAGED_LOOKASIDE_LIST 構造体は、ページ バッファーを含むルックアサイド リストを記述します。 ドライバーは、このルックアサイド リストのカスタム割り当て解放ルーチンを提供する場合、これらのルーチンは、入力パラメーターとしてプライベート コンテキストを受け取りません。 このため、ドライバーが Windows Vista 以降のバージョンの Windows でのみ実行させたい場合は、ルックアサイド リストには PAGED_LOOKASIDE_LIST 構造体ではなく、LOOKASIDE_LIST_EX 構造体を使用することを検討してください。 次のシステムで提供されるルーチンは、PAGED_LOOKASIDE_LIST 構造体によって記述されるルックアサイド リストをサポートします。

ExAllocateFromPagedLookasideList

ExDeletePagedLookasideList

ExFreeToPagedLookasideList

ExInitializePagedLookasideList

Windows 2000 以降、 NPAGED_LOOKASIDE_LIST 構造体は、非ページ バッファーを含むルックアサイド リストを記述します。 ドライバーは、このルックアサイド リストのカスタム割り当て解放ルーチンを提供する場合、これらのルーチンは、入力パラメーターとしてプライベート コンテキストを受け取りません。 繰り返しますが、ドライバーが Windows Vista 以降のバージョンの Windows でのみ実行させたい場合は、ルックアサイド リストには NPAGED_LOOKASIDE_LIST 構造体ではなく、LOOKASIDE_LIST_EX 構造体を使用することを検討してください。 次のシステムで提供されるルーチンは、NPAGED_LOOKASIDE_LIST 構造体によって記述されるルックアサイド リストをサポートします。

ExAllocateFromNPagedLookasideList

ExDeleteNPagedLookasideList

ExFreeToNPagedLookasideList

ExInitializeNPagedLookasideList

実装のガイドライン

LOOKASIDE_LIST_EX 構造体を使用するルックアサイド リストを実装するには、次の設計ガイドラインに従います。

  • ExInitializeLookasideListEx を呼び出して、ルックアサイド リストを設定します。 この呼び出しでは、ルックアサイド リスト内のエントリをページ バッファーと非ページ バッファーのどちらにするかを指定します。 ドライバー自体、またはそのルックアサイド リスト エントリを渡す基になるドライバーが IRQL >= DISPATCH_LEVEL でこれらのエントリにアクセスする可能性がある場合は、非ページ バッファーを使用します。 ドライバーのルックアサイド リスト エントリへのアクセスが常に IRQL <= APC_LEVELで発生する場合にのみページ バッファーを使用します。

  • ルックアサイド リストの LOOKASIDE_LIST_EX 構造体は、リスト内のエントリがページングされているか非ページであるかに関係なく、常に非ページ システム メモリに存在する必要があります。

  • パフォーマンスを向上させるには、割り当てパラメーターと解放パラメーターの NULL ポインターを ExInitializeLookasideListEx に渡します。ただし、割り当ておよび割り当て解除ルーチンが、単にルックアサイド リスト エントリにメモリを割り当て、解放する以上のことを行わなければならない場合を除きます。 たとえば、これらのルーチンは、動的に割り当てられたバッファーのドライバーの使用状況に関する情報を記録することがあります。

  • ドライバーが提供する割り当てルーチンは、ExAllocatePoolWithTag ルーチンまたは ExAllocatePoolWithQuotaTag ルーチンに直接受け取る入力パラメーター (PoolTypeタグ、およびサイズ) を渡して、新しいバッファーを割り当てることができます。

  • ExAllocateFromLookasideListEx を呼び出すたびに、以前に割り当てられたエントリが使用されなくなったら、できるだけ早く ExFreeToLookasideListEx の相互呼び出しを行います。

ExAllocatePoolWithTagExFreePool をそれぞれ呼び出す以外に何もしない割り当てルーチンと解放ルーチンを指定すると、プロセッサ サイクルが無駄になります。 ExAllocateFromLookasideListEx は、ドライバーが ExInitializeLookasideListExNULL割り当てポインターと解放ポインターを渡すと、ExAllocatePoolWithTagExFreePool に必要な呼び出しを自動的に行います。

ドライバーが提供する 割り当て ルーチンは、ページ プールからのエントリが非ページ ルックアサイド リストに保持されるようにメモリを割り当ててはいけません。また、その逆も同様です。 また、固定サイズのエントリも割り当てる必要があります。なぜなら、それ以降の ExAllocateFromLookasideListEx へのドライバー呼び出しでは、リストが空でない限り、ルックアサイド リストに現在保持されている最初のエントリが返されるためです。 つまり、ExAllocateFromLookasideListEx を呼び出すと、指定されたルックアサイド リストが現在空の場合にのみ、ドライバーが指定した割り当てルーチンの呼び出しをします。 したがって、ExAllocateFromLookasideListEx 呼び出すたびに、返されるエントリは、ルックアサイド リスト内のすべてのエントリが固定サイズである場合にのみ、ドライバーが必要とする正確なサイズになります。 ドライバーが提供する割り当てルーチンは、ドライバーが ExInitializeLookasideListEx に最初に渡したタグ値も変更しないでください。プール タグ値を変更すると、ドライバーのメモリ使用量のデバッグと追跡がが難しくなるからです。

ExFreeToLookasideListEx の呼び出しは、リストが既にフルになっている場合を除き、以前に割り当てられたエントリをルックアサイド リストに格納します (つまり、リストには、システムによって決定されたエントリの最大数が含まれています)。 パフォーマンスを向上させるために、ドライバーは ExAllocateFromLookasideListEx に対して行うすべての呼び出しに対して、可能な限り迅速に ExFreeToLookasideListEx に対して相互呼び出しを行う必要があります。 ドライバーがルックアサイド リストにすばやくエントリを解放すると、そのドライバーが ExAllocateFromLookasideListEx を次に呼び出したときに、新しいエントリのメモリを動的に割り当てるパフォーマンスが低下しにくくなります。

同様のガイドラインは、PAGED_LOOKASIDE_LIST または NPAGED_LOOKASIDE_LIST 構造体を使用するルックアサイド リストに適用されます。