次の方法で共有


中間レベル ドライバーでの IRP の処理

上位レベルのドライバーには、下位レベルのデバイス ドライバーとは異なる標準ルーチンのセットがあり、両方の種類のドライバーに共通する標準ルーチンのサブセットが重複しています。

中間および最上位レベルのドライバーのルーチンのセットも、次の条件に従って異なります。

  • 基になる物理デバイスの性質

  • 基になるデバイス ドライバーがデバイス オブジェクトをダイレクト I/O 用に設定するか、またはバッファー I/O 用に設定するか

  • 個々の上位レベルのドライバーの設計

次の図は、前のセクションで説明した最下位レベルのデバイス ドライバー上に階層化された中間ミラー ドライバーの標準ルーチンを介して IRP が通る可能性があるパスを示しています。

次の図に示すドライバーには、次の特性があります。

  • ドライバーは、複数の物理デバイス上に、また場合によっては複数のデバイス ドライバー上に階層化されます。

  • ドライバーは、入力 IRP で要求された操作に応じて、下位レベルのドライバーに対して追加の IRP を割り当てる場合があります。

  • ドライバーの上には少なくとも 1 つ、このドライバーよりも上位レベルで他の中間ドライバーの上に階層化されている可能性があるファイル システム ドライバーが階層化されています。

diagram illustrating an irp path through intermediate driver routines.

この図に示すように、I/O マネージャーは IRP を作成し、指定された主要な関数コードのドライバーのディスパッチ ルーチンに送信します。 この関数コードが IRP_MJ_WRITE であるとすれば、ディスパッチ ルーチンは DDDispatchWrite になります。 中間ドライバーの I/O スタックの場所は中央に表示され、上位レベルと下位レベルのドライバーの不定数の I/O スタックの場所は影付きで表示されています。

IRP の割り当て

ミラー ドライバーの目的は、複数の物理デバイスへの書き込み要求を送信し、これらのデバイスのドライバーに代わって読み取り要求を送信することです。 書き込み要求の場合、ドライバーは、入力 IRP のパラメーターが有効であると仮定して、データを書き込むデバイスごとに重複する IRP を作成します。

前の図は、IoAllocateIrp の呼び出しを示していますが、上位レベルのドライバーは他のサポート ルーチンを呼び出して、下位レベルのドライバーに対する IRP を割り当てることができます。 「下位レベル ドライバー用 IRP の作成」を参照してください 。

ディスパッチ ルーチンは、IoAllocateIrp を呼び出すときに、IRP に必要な I/O スタックの場所の数を指定します。 ドライバーは、チェーン内の下位ドライバーごとにスタックの場所を指定し、ミラー ドライバーのすぐ下にある各ドライバーのデバイス オブジェクトから適切な値を取得する必要があります。 必要に応じて、ドライバーは、前の図のドライバーのように、割り当てる IRP ごとに独自のスタックの場所を取得する IoAllocateIrp を呼び出すときに、この値に 1 を加算することができます。

この中間ドライバーのディスパッチ ルーチンは、元の IRP で IoGetCurrentIrpStackLocation (図示されていません) を呼び出して、パラメーターをチェックします。

新しく作成された各 IRP と IoGetCurrentIrpStackLocation に独自のスタックの場所を割り当て、後で IoCompletion ルーチンで使用するコンテキストを自身で作成するために、IoSetNextIrpStackLocation を呼び出します。

次に、新しく作成された各 IRP で IoGetNextIrpStackLocation を呼び出して、割り当てた IRP で次の下位レベルのドライバーの I/O スタックの場所を設定できるようにします。 ミラー ドライバーのディスパッチ ルーチンは、IRP 関数コードとパラメーター (転送バッファーへのポインター、IRP_MJ_WRITE 用に転送されるバイト単位の長さ) を、次の下位ドライバーの I/O スタックの場所にコピーします。 これらのドライバーは、ドライバーの I/O スタックの場所がすぐ下にある場合は、その場所を設定します。

IoSetCompletionRoutine と IoCallDriver の呼び出し

前の図のディスパッチ ルーチンは、割り当てた IRP ごとに IoSetCompletionRoutine を呼び出します。 前の図のドライバーは割り当てた IRP を破棄する必要があるため、このドライバーは、I/O 操作が正常に完了したか、失敗したか、取り消されたかに関係なく、下位のドライバーがその IRP を完了したときに、その IoCompletion ルーチンを呼び出すように設定します。

前の図のドライバーは並列にミラーするため、ミラー パーティションを表すターゲット デバイス オブジェクトごとに 1 回ずつ、2 回 IoCallDriver を呼び出して、割り当てた両方の IRP を次の下位レベルのドライバーに渡します。

ドライバーの IoCompletion ルーチンでの IRP の処理

下位レベルのドライバーのいずれかのセットが要求された操作を完了すると、I/O マネージャーは、中間ミラー ドライバーの IoCompletion ルーチンを呼び出します。 ミラー ドライバーは、下位ドライバーが重複するすべての IRP を完了したタイミングを追跡するために、元の IRP の独自の I/O スタックの場所にカウントを維持します。

下位ドライバーの一方のセットが前の図に示した重複した IRP を完了したことを I/O 状態ブロックが示していると仮定して、ミラー ドライバーの IoCompletion ルーチンはカウントをデクリメントしますが、カウントを 0 にデクリメントするまでは、元の IRP を完了できません。 デクリメントされたカウントがまだ 0 でなければ、IoCompletion ルーチンは、ドライバーが割り当てた、最初に返された IRP (前の図の DupIRP1) で IoFreeIrp を呼び出し、STATUS_MORE_PROCESSING_REQUIRED を返します。

前の図に示した DupIRP2 を使用して、ミラー ドライバーの IoCompletion ルーチンが再度呼び出されると、IoCompletion ルーチンは元の IRP のカウントをデクリメントし、下位レベルのドライバーの両方のセットで要求された操作を実行したと判断します。

DupIRP2 の I/O 状態ブロックも STATUS_SUCCESS で設定されていると仮定して、IoCompletion ルーチンは、DupIRP2 から元の IRP に I/O 状態ブロックをコピーし、DupIRP2 を解放します。 元の IRP で IoCompleteRequest を呼び出し、STATUS_MORE_PROCESSING_REQUIRED を返します。 この状態を返した場合、I/O マネージャーは DupIRP2 でそれ以上の完了処理は試行しません。IRP はスレッドに関連付けられていないため、完了処理は、それを作成したドライバーで終了する必要があります。

下位レベルのドライバーのセットがどちらもミラー ドライバーの IRP を正常に完了しない場合、ミラー ドライバーの IoCompletion ルーチンはエラーをログに記録し、適切なミラー データ復旧を行う必要があります。 詳細については、「エラーのログ記録」を参照してください。