I/O 요청 대기 강제 옵션은 IoCallDriver에 대한 드라이버의 호출에 응답하여 STATUS_PENDING을 임의로 반환합니다. 이 옵션은 IoCallDriver에서 STATUS_PENDING 반환 값에 응답하기 위한 드라이버의 논리를 테스트합니다.
이 옵션은 Windows Vista 이상 버전의 Windows 운영 체제에서만 지원됩니다.
주의 드라이버 작동에 대한 자세한 지식이 있고 드라이버가 IoCallDriver에 대한 모든 호출에서 STATUS_PENDING 반환 값을 처리하도록 디자인되었는지 확인하지 않는 한 드라이버에서 이 옵션을 사용하지 마세요. 모든 호출에서 STATUS_PENDING 처리하도록 설계되지 않은 드라이버에서 이 옵션을 실행하면 디버그하거나 수정하기 어려울 수 있는 크래시, 메모리 손상 및 비정상적인 시스템 동작이 발생할 수 있습니다.
강제 보류 중인 I/O 요청을 사용하는 이유는 무엇인가요?
드라이버 스택의 상위 수준 드라이버는 IoCallDriver 를 호출하여 IRP를 드라이버 스택의 하위 수준 드라이버로 전달합니다. 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를 호출하기 전에 드라이버는 IoBuildSynchronousFsdRequest를 호출하여 IRP의 동기 처리를 준비해야 합니다.
IoCallDriver가 STATUS_PENDING 반환하는 경우 드라이버는 지정된 이벤트에서 KeWaitForSingleObject를 호출하여 IRP가 완료될 때까지 기다려야 합니다.
드라이버는 I/O 관리자가 이벤트를 알리기 전에 IRP가 해제될 수 있음을 예상해야 합니다.
IoCallDriver를 호출한 후 호출자는 IRP를 참조할 수 없습니다.
강제로 보류 중인 I/O 요청을 통해 감지할 수 있는 오류는 무엇입니까?
드라이버가 IoCallDriver를 호출하고 STATUS_PENDING 반환 값을 수신할 때, 강제로 보류 중인 I/O 요청 옵션은 다음과 같은 오류를 감지합니다.
드라이버는 동기 처리를 위해 정렬하기 위해 IoBuildSynchronousFsdRequest 를 호출하지 않습니다.
드라이버는 KeWaitForSingleObject를 호출하지 않습니다.
드라이버는 IoCallDriver를 호출한 후 IRP 구조의 값을 참조합니다. IoCallDriver를 호출한 후 상위 수준 드라이버는 완료 루틴을 설정한 다음 모든 하위 수준 드라이버가 IRP를 완료한 경우에만 IRP에 액세스할 수 없습니다. IRP가 해제되면 드라이버가 충돌합니다.
드라이버가 관련 함수를 잘못 호출합니다. 예를 들어 드라이버는 KeWaitForSingleObject 를 호출하고 이벤트 개체에 포인터를 전달하는 대신 이벤트(Object 매개 변수)에 핸들을 전달합니다.
드라이버가 잘못된 이벤트를 기다립니다. 예를 들어 드라이버는 IoSetCompletionRoutine을 호출하지만 IRP가 완료될 때 I/O 관리자가 신호를 받는 IRP 이벤트를 기다리는 대신 자체 완료 루틴에서 신호를 받는 내부 이벤트를 기다립니다.
Windows 7에 도입된 보류 중인 I/O 요청 변경 내용 강제 적용
Windows 7부터 보류 중인 I/O 요청 강제 적용 옵션은 확인된 드라이버에서 STATUS_PENDING 코드 경로를 강제로 적용하는 데 더 효과적입니다. 이전 Windows 버전에서 드라이버 검증 도구는 IRP에 대한 첫 번째 IoCompleteRequest 가 실행되는 경우에만 IRP 완성을 지연하도록 강제했습니다. 즉, 동일한 디바이스 스택에서 Driver2의 동작으로 Driver1을 확인하는 효과를 줄일 수 있습니다. Driver2는 디스패치 루틴에서 Driver1로 반환되기 전에 완료를 위해 동기적으로 대기할 수 있습니다. IRP 완료의 강제 지연은 I/O 요청이 완료 경로에서 확인된 드라이버로 정확히 복구되기 전에 발생합니다. 즉, 확인된 드라이버의 STATUS_PENDING 코드 경로가 실제로 실행되고 확인된 드라이버가 완료 지연을 인식합니다.
이 옵션 활성화
강제 보류 중인 I/O 요청을 활성화하려면 I/O 확인도 활성화해야 합니다. 드라이버 검증 도구 관리자 또는 Verifier.exe 명령줄을 사용하여 하나 이상의 드라이버에 대해 보류 중인 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이 설정은 즉시 유효하지만 컴퓨터를 종료하거나 다시 부팅하면 손실됩니다. 자세한 내용은 Volatile 설정 사용을 참조하세요.
드라이버 확인 관리자를 사용하여
- 드라이버 검증 도구 관리자를 시작합니다. 명령 프롬프트 창에 검증 도구를 입력합니다.
- 사용자 지정 설정 만들기(코드 개발자용)를 선택하고 다음을 클릭합니다.
- 전체 목록에서 개별 설정 선택을 선택합니다.
- 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을 완료하려고 했음을 보여줍니다. 드라이버 검증 도구가 지연된 완료를 강제로 실행했기 때문에 Acpi.sys는 pci!PciCallDownIrpStack에 STATUS_PENDING을 반환했습니다. 이 호출로 인해 충돌이 발생한 경우, 드라이버 소유자는 pci!PciCallDownIrpStack에 대한 소스 코드를 검토하고 STATUS_PENDING을 올바르게 처리하도록 수정해야 합니다.