보류 중인 I/O 요청 강제 적용

보류 중인 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 요청을 강제로 검색하는 오류는 무엇인가요?

보류 중인 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부터 보류 중인 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)만 활성화하려는 경우 드라이버 검증 도구는 0x200(강제 보류 중인 I/O 요청) 및 I/O 확인을 모두 자동으로 사용하도록 설정합니다.

    명령에 /volatile 매개 변수를 추가하여 컴퓨터를 다시 부팅하지 않고 보류 중인 I/O 요청 강제 활성화 및 비활성화할 수도 있습니다. 예:

    verifier /volatile /flags 0x210 /adddriver MyDriver.sys
    

    이 설정은 즉시 유효하지만 컴퓨터를 종료하거나 다시 부팅하면 손실됩니다. 자세한 내용은 Volatile 설정 사용을 참조하세요.

  • 드라이버 검증 도구 관리자 사용

    1. 드라이버 검증 도구 관리자를 시작합니다. 명령 프롬프트 창에 검증 도구 를 입력합니다.
    2. 사용자 지정 설정 만들기(코드 개발자용)를 선택하고 다음을 클릭합니다.
    3. 전체 목록에서 개별 설정 선택을 선택합니다.
    4. 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에 STATUS_PENDING 반환했습니다 . PciCallDownIrpStack. 이 호출로 인해 충돌이 발생한 경우 드라이버 소유자는 pci에 대한 소스 코드를 검토해야 합니다 . PciCallDownIrpStack 을 수정하여 STATUS_PENDING 올바르게 처리합니다.