保留中の I/O 要求を強制する
[保留中の I/O 要求を強制] オプションは、ドライバーによる IoCallDriverの呼び出しに応答して、STATUS_PENDINGをランダムに返します。 このオプションは、 IoCallDriver からの戻り値STATUS_PENDINGに応答するためのドライバーのロジックをテストします。
このオプションは、Windows Vista 以降のバージョンの Windows オペレーティング システムでのみサポートされます。
注意 ドライバーの操作に関する詳細な知識があり、ドライバーが IoCallDriver へのすべての呼び出しからのSTATUS_PENDING戻り値を処理するように設計されていることを確認していない限り、ドライバーではこのオプションを使用しないでください。 すべての呼び出しからのSTATUS_PENDINGを処理するように設計されていないドライバーでこのオプションを実行すると、クラッシュ、メモリ破損、および通常とは異なるシステム動作が発生し、デバッグや修正が困難になる可能性があります。
保留中の I/O 要求を強制を使用する理由
ドライバー スタックの上位レベルのドライバーは、ドライバー スタック内の下位レベルのドライバーに IRP を渡すために IoCallDriver を呼び出します。 IRP を受け取る下位レベルのドライバーディスパッチ ルーチンは、IRP をすぐに完了するか、STATUS_PENDINGを返して、後で IRP を完了することができます。
通常、呼び出し元は、どちらの結果も処理できるように準備する必要があります。 ただし、ほとんどのディスパッチ ルーチンは IRP を直ちに処理するため、呼び出し元のSTATUS_PENDING ロジックは頻繁に実行されず、重大なロジック エラーが検出されない可能性があります。 [保留中の I/O 要求を強制] オプションは、 IoCallDriver への呼び出しをインターセプトし、呼び出し元ドライバーの使用頻度の低いロジックをテストするSTATUS_PENDINGを返します。
保留中の I/O 要求を強制を使用するタイミング
このテストを実行する前に、ドライバーの設計とソース コードを確認し、ドライバーがそのすべての IoCallDriver 呼び出しからのSTATUS_PENDINGを処理することを目的としていることを確認します。
多くのドライバーは、 IoCallDriver のすべての呼び出しでSTATUS_PENDINGを処理するようには設計されていません。 IRP をすぐに完了することが保証されている特定の既知のドライバーに IRP を送信している可能性があります。 ドライバーを処理しないドライバーにSTATUS_PENDINGを送信すると、ドライバーとシステムのクラッシュやメモリの破損が発生する可能性があります。
ドライバーはSTATUS_PENDINGをどのように処理する必要がありますか?
IoCallDriver を呼び出す上位レベルのドライバーは、次のようにSTATUS_PENDING戻り値を処理する必要があります。
IoCallDriverを呼び出す前に、ドライバーは、IRP の同期処理を配置する IoBuildSynchronousFsdRequest を呼び出す必要があります。
IoCallDriver がSTATUS_PENDINGを返す場合、ドライバーは、指定したイベントで KeWaitForSingleObject を呼び出すことによって IRP の完了を待機する必要があります。
ドライバーは、I/O マネージャーがイベントを通知する前に IRP が解放される可能性があることを予測する必要があります。
IoCallDriver を呼び出した後、呼び出し元は IRP を参照できません。
保留中の I/O 要求を強制で検出されるエラーはどれですか?
[保留中の I/O 要求を強制] オプションは、 IoCallDriver を呼び出し、STATUS_PENDING戻り値を受け取るドライバーで次のエラーを検出します。
ドライバーは、同期処理を配置するために IoBuildSynchronousFsdRequest を呼び出しません。
ドライバーは KeWaitForSingleObject を呼び出しません。
ドライバーは、 IoCallDriver を呼び出 した後、IRP 構造体の値を参照します。 IoCallDriver を呼び出した後、上位レベルのドライバーは、完了ルーチンを設定し、その後、すべての下位レベルのドライバーが IRP を完了した場合にのみ、IRP にアクセスできません。 IRP が解放されると、ドライバーがクラッシュします。
ドライバーは、関連する関数を正しく呼び出しません。 たとえば、ドライバーは KeWaitForSingleObject を呼び出し、イベント オブジェクトへのポインターを渡すのではなく、(Object パラメーターとして) イベントにハンドルを渡します。
ドライバーは間違ったイベントを待機します。 たとえば、ドライバーは IoSetCompletionRoutineを呼び出しますが、IRP が完了したときに I/O マネージャーによって通知される IRP イベントを待機するのではなく、独自の完了ルーチンによって通知される内部イベントを待機します。
Windows 7 で導入された保留中の I/O 要求の変更を強制する
Windows 7 以降では、[Force Pending I/O Requests]\(保留中の I/O 要求の強制\) オプションは、検証済みドライバーでSTATUS_PENDING コード パスを強制的に実行する場合に効果的です。 以前のバージョンの Windows では、ドライバー検証ツールは、その IRP の最初の IoCompleteRequest が実行された場合にのみ、IRP の完了を遅延させる必要があります。 つまり、Driver1 の検証の有効性は、同じデバイス スタックからの Driver2 の動作によって軽減できます。 Driver2 は、ディスパッチ ルーチンから Driver1 に戻る前に、完了を同期的に待機する場合があります。 IRP 完了の強制遅延は、I/O 要求が完了パスの検証済みドライバーにアンワインドする直前に正確に発生します。 つまり、検証済みドライバーのSTATUS_PENDING コード パスが実際に実行され、検証済みドライバーは完了の遅延を認識します。
このオプションのアクティブ化
保留中の I/O 要求を強制をアクティブにするには、 I/O 検証もアクティブ化する必要があります。 ドライバー検証ツール マネージャーまたは Verifier.exe コマンド ラインを使用して、1 つ以上のドライバーの保留中の I/O 要求の強制オプションをアクティブにすることができます。 詳細については、 「ドライバー検証ツール オプションの選択」を参照してください 。
[保留中の I/O 要求を強制] オプションは、Windows Vista 以降のバージョンの Windows でのみサポートされています。
コマンドラインで
保留中の I/O 要求を強制をアクティブにするには、0x210のフラグ値を使用するか、フラグ値に0x210を追加します。 この値は、I/O 検証 (0x10) と強制保留中の I/O 要求 (0x200) をアクティブにします。
次に例を示します。
verifier /flags 0x210 /driver MyDriver.sys
このオプションは、次の起動時にアクティブになります。
強制保留中の I/O 要求 (検証ツール/フラグ 0x200) のみをアクティブ化しようとすると、ドライバー検証ツールによって、保留中の I/O 要求を強制 (0x200) と I/O 検証の両方が自動的に有効になります。
/volatile パラメーターをコマンドに追加することで、コンピューターを再起動せずに、保留中の I/O 要求を強制をアクティブ化および非アクティブ化することもできます。 次に例を示します。
verifier /volatile /flags 0x210 /adddriver MyDriver.sys
この設定はすぐに有効になりますが、コンピューターをシャットダウンまたは再起動すると失われます。 詳細については、「揮発性設定の使用」を参照してください。
ドライバー検証マネージャーの使用
- ドライバー検証マネージャーを起動します。 コマンド プロンプト ウィンドウに 「Verifier」 と入力します。
- [カスタム設定の作成 (コード開発者用)] を選択し、 [次へ] をクリックします。
- 全一覧から [個々の設定を選択] を選択します。
- [I/O 検証] と [保留中の I/O 要求を強制] を選択 します。
[保留中の I/O 要求を強制] のみを選択した場合、ドライバー検証ツール マネージャーは I/O 検証 が必要であることを通知し、有効にするためのオファーを表示します。
結果の表示
強制保留中の I/O 要求テストの結果を表示するには、フラグ値が 0x40 の !verifier デバッガー拡張機能を使用します。
!verifier の詳細については、 Windows 用デバッグ ツール のドキュメントの !verifier のトピックを参照してください。
保留中の I/O 要求を強制テストの結果としてテスト マシンがクラッシュした場合は、 !verifier 40 コマンドを使用して原因を見つけることができます。 現在のスタック トレースで、ドライバーによって最近使用された IRP のアドレスを見つけます。 たとえば、スレッドのスタック フレームを 表示する kP コマンドを使用する場合は、現在のスタック トレースの関数パラメーターの中から IRP アドレスを見つけることができます。 次に、 !verifier 40 を実行し、IRP のアドレスを探します。 最新の保留中のスタック トレースを強制がディスプレイの上部に表示されます。
たとえば、次のPci.sysのスタック トレースは、保留中の I/O 要求を強制への応答を示しています。 テストでは、Pci.sys ロジックのエラーは表示されません。
kd> !verifier 40
# Size of the log is 0x40
========================================================
IRP: 8f84ef00 - forced pending from stack trace:
817b21e4 nt!IovpLocalCompletionRoutine+0xb2
81422478 nt!IopfCompleteRequest+0x15c
817b2838 nt!IovCompleteRequest+0x9c
84d747df acpi!ACPIBusIrpDeviceUsageNotification+0xf5
84d2d36c acpi!ACPIDispatchIrp+0xe8
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
817c6a9d nt!ViFilterDispatchPnp+0xe9
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
84fed489 pci!PciCallDownIrpStack+0xbf
84fde1cb pci!PciDispatchPnpPower+0xdf
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
817c6a9d nt!ViFilterDispatchPnp+0xe9
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
84ff2ff5 pci!PciSendPnpIrp+0xbd
84fec820 pci!PciDevice_DeviceUsageNotification+0x6e
84fde1f8 pci!PciDispatchPnpPower+0x10c
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
84d76ce2 acpi!ACPIFilterIrpDeviceUsageNotification+0x96
84d2d36c acpi!ACPIDispatchIrp+0xe8
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
84f7f16c PCIIDEX!PortWdmForwardIrpSynchronous+0x8e
84f7b2b3 PCIIDEX!GenPnpFdoUsageNotification+0xcb
84f7d301 PCIIDEX!PciIdeDispatchPnp+0x45
817b258f nt!IovCallDriver+0x19d
8142218e nt!IofCallDriver+0x1c
スタック トレースは、 Acpi.sys が IRP 8f84ef00 を完了しようとしたことを示しています。 。Driver Verifierが遅延完了を強制したため、 Acpi.sys は pci!PciCallDownIrpStackにSTATUS_PENDINGを返しました。 この呼び出しによってクラッシュが発生した場合、ドライバーの所有者は pci!PciCallDownIrpStack のソース コードを確認し、STATUS_PENDING を適切に処理できるように修正する必要があります。