다음을 통해 공유


보류 중인 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 요청을 통해 감지할 수 있는 오류는 무엇입니까?

드라이버가 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 설정 사용을 참조하세요.

  • 드라이버 확인 관리자를 사용하여

    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.syspci!PciCallDownIrpStack에 STATUS_PENDING을 반환했습니다. 이 호출로 인해 충돌이 발생한 경우, 드라이버 소유자는 pci!PciCallDownIrpStack에 대한 소스 코드를 검토하고 STATUS_PENDING을 올바르게 처리하도록 수정해야 합니다.