I/O スタック位置

I/O マネージャーは、階層型ドライバーのチェーン内の各ドライバーに、セットアップされるすべての IRP の I/O スタックの場所を提供します。 各 I/O スタックの場所は、IO_STACK_LOCATION 構造で構成されています。

I/O マネージャーは、階層型ドライバーのチェーン内の各ドライバーに対応する配列要素を持つ、各 IRP の I/O スタックの場所の配列を作成します。 各ドライバーは、パケット内のスタックの場所のいずれかを所有し、IoGetCurrentIrpStackLocation を呼び出して、I/O 操作に関するドライバー固有の情報を取得します。

このようなチェーン内の各ドライバーは、IoGetNextIrpStackLocation を呼び出した後、次に下位のドライバーの I/O スタックの場所をセットアップします。 上位レベルのドライバーの I/O スタックの場所を使用すると、ドライバーの IoCompletion ルーチンがクリーンアップ操作を実行できるように、操作に関するコンテキストを格納することもできます。

階層型ドライバーでの IRP の処理」の図は、ファイル システム ドライバーと大容量記憶装置デバイス ドライバーの 2 つのドライバーを示しているため、元の IRP の 2 つの I/O スタックの場所を示しています。 「階層型ドライバー図での IRP の処理」の図にあるドライバー割り当て IRP には、それらを作成した FSD (ファイル システム ドライバー) のスタックの場所がありません。 下位レベルのドライバーに IRP を割り当てる上位レベルのドライバーは、次の下位ドライバーのデバイス オブジェクトの StackSize 値に従って、新しい IRP に必要な I/O スタックの場所の数も決定します。

次の図は、IRP の内容の詳細を示しています。

diagram illustrating the contents of i/o stack location in an irp.

図に示すように、IRP の各ドライバー固有の I/O スタックの場所には、以下の一般的な情報が含まれています。

  • ドライバーが実行する必要がある基本操作を示す、メジャー関数コード (IRP_MJ_XXX)

  • FSD、上位レベルの SCSI ドライバー、およびすべての PnP ドライバーによって処理される一部の主要な関数コードについては、ドライバーが実行する必要がある基本操作のサブケースを示すマイナー関数コード (IRP_MN_XXX)

  • ドライバーがデータを転送するバッファーの長さと開始位置など、操作固有の引数のセット

  • 要求された操作のターゲット (物理、論理、または仮想) デバイスを表す、ドライバーによって作成されたデバイス オブジェクトへのポインター

  • 開いているファイル、デバイス、ディレクトリ、またはボリュームを表すファイル オブジェクトへのポインター

    ファイル システム ドライバーは、IRP 内の I/O スタックの場所を通じてファイル オブジェクトにアクセスします。 通常、他のドライバーはファイル オブジェクトを無視します。

特定のドライバーによって処理される IRP のメジャーおよびマイナー関数コードのセットは、デバイスの種類に固有です。 ただし、最下位レベルのドライバーと中間ドライバー (PnP 関数とフィルター ドライバーを含む) は通常、次の基本要求のセットを処理します。

  • IRP_MJ_CREATE — ターゲット デバイス オブジェクトを開き、存在していて I/O 操作で使用可能であることを示します。

  • IRP_MJ_READ — デバイスからデータを転送する

  • IRP_MJ_WRITE — デバイスにデータを転送する

  • IRP_MJ_DEVICE_CONTROL — システム定義のデバイスの種類固有の I/O 制御コード (IOCTL) に従って、デバイスをセットアップ (またはリセット) します。

  • IRP_MJ_CLOSE — ターゲット デバイス オブジェクトを閉じます

  • IRP_MJ_PNP — デバイスでプラグ アンド プレイ操作を実行します。 IRP_MJ_PNP 要求は、PnP マネージャーによって I/O マネージャーを通じて送信されます。

  • IRP_MJ_POWER — デバイスで電源操作を実行します。 IRP_MJ_POWER 要求は、電源マネージャーにより I/O マネージャーを通じて送信されます。

ドライバーが処理のために必要とするメジャー IRP 関数コードについて詳しくは、「IRP のメジャー関数コード」をご覧ください。

ファイル システムが大容量記憶装置用の他のドライバーに重なっているため、I/O マネージャーは通常、少なくとも 2 つの I/O スタックの場所を持つ IRP を大容量記憶装置デバイス ドライバーに送信します。 I/O マネージャーは、スタック位置 1 つ持つ IRP を、その上で他のドライバーが階層化されていないドライバーに送信します。

ただし、I/O マネージャーは、システム内の既存のドライバーのチェーンに新しいドライバーを追加するためのサポートを提供します。 たとえば、特定のディスク パーティション上のデータをバックアップする中間ミラー ドライバーは、「階層型ドライバーでの IRP の処理」の図に示されているファイル システム ドライバーや最下位レベルのドライバーなど、ドライバーのペアの間に挿入される場合があります。 この新しいドライバーがデバイス スタックにアタッチされると、I/O マネージャーは、ファイル システム、ミラー、および最下位レベルのドライバーに送信するすべての IRP の I/O スタックの場所の数を調整します。 「階層型ドライバーでの IRP の処理」の図でファイル システムが割り当てられたすべての IRP には、このような新しいミラー ドライバーの別の I/O スタックの場所も含まれています。

既存のチェーンに新しいドライバーを追加するこのサポートは、IRP の I/O スタックの場所への特定のドライバーのアクセスに特定の制限があることを意味します。

  • 階層型ドライバーのチェーン内の上位レベルのドライバーは、あらゆる IRP にある自身のドライバーと次の下位レベルのドライバーの I/O スタックの場所にのみ安全にアクセスできます。 このようなドライバーは、IRP の次の下位レベルのドライバーの I/O スタックの場所をセットアップする必要があります。 ただし、このような上位レベルのドライバーを設計する場合、新しいドライバーがドライバーのすぐ下の既存のチェーンに追加されるタイミング (または追加されるかどうか) を予測することはできません。

    したがって、置き換えられた次の下位レベルのドライバーと同じ IRP メジャー関数コード (IRP_MJ_XXX) を、後で追加されたドライバーが処理すると想定する必要があります。

  • 階層型ドライバーのチェーン内の最下位レベルのドライバーは、任意の IRP にある自身の I/O スタックの場所にのみ安全にアクセスできます。 このようなドライバーを設計するとき、新しいドライバーがデバイス ドライバーの上の既存のチェーンに追加されるタイミング (または追加されるかどうか) を予測することはできません。

    最下位レベルのドライバーを設計するときは、特定の IRP の元のソースが何であるか、およびその上に階層化されるドライバーの数に関係なく、ドライバーが自身の I/O スタックの場所で渡された情報を使って IRP の処理を続けることができると仮定してください。