하드웨어 우선 순위 관리
드라이버 루틴이 실행되는 IRQL은 호출할 수 있는 커널 모드 드라이버 지원 루틴을 결정합니다. 예를 들어 일부 드라이버 지원 루틴에서는 호출자가 IRQL = DISPATCH_LEVEL 실행되어야 합니다. 호출자가 PASSIVE_LEVEL보다 높은 IRQL에서 실행 중인 경우에는 다른 호출을 안전하게 호출할 수 없습니다.
다음은 가장 일반적으로 구현되는 표준 드라이버 루틴이 호출되는 IRQL 목록입니다. IRQL은 가장 낮은 우선 순위에서 가장 높은 우선 순위로 나열됩니다.
PASSIVE_LEVEL
마스킹된 인터럽트 - 없음.
에서 호출되는 드라이버 루틴 PASSIVE_LEVEL — DriverEntry, AddDevice, 다시 초기화, 언로드 루틴, 대부분의 디스패치 루틴, 드라이버 생성 스레드, 작업자 스레드 콜백.
APC_LEVEL
마스킹된 인터럽트 - APC_LEVEL 인터럽트는 마스킹 해제됩니다.
에서 호출되는 드라이버 루틴 APC_LEVEL — 일부 디스패치 루틴( 디스패치 루틴 및 IRQL 참조).
DISPATCH_LEVEL
마스킹된 인터럽트 - DISPATCH_LEVEL 및 APC_LEVEL 인터럽트는 마스킹 해제됩니다. 디바이스, 클록 및 전원 오류 중단이 발생할 수 있습니다.
에서 호출되는 드라이버 루틴 DISPATCH_LEVEL — StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (취소 스핀 잠금을 보유하는 동안), DpcForIsr, CustomTimerDpc, CustomDpc 루틴.
DIRQL
마스킹된 인터럽트 - 드라이버 인터럽트 개체의 IRQL <= DIRQL의 모든 인터럽트입니다. DIRQL 값이 더 높은 디바이스 인터럽트는 클록 및 전원 오류 인터럽트와 함께 발생할 수 있습니다.
DIRQL에서 호출되는 드라이버 루틴 - InterruptService, SynchCritSection 루틴.
APC_LEVEL PASSIVE_LEVEL 간의 유일한 차이점은 APC_LEVEL 실행 중인 프로세스가 APC 인터럽트에서 가져올 수 없다는 것입니다. 그러나 두 IRQL 모두 스레드 컨텍스트를 의미하며 둘 다 코드를 페이징할 수 있음을 의미합니다.
가장 낮은 수준의 드라이버는 세 가지 IRQL 중 하나에서 실행되는 동안 IRP를 처리합니다.
PASSIVE_LEVEL 드라이버의 디스패치 루틴에서 프로세서에서 마스킹된 인터럽트 없이
DriverEntry, AddDevice, Reinitialize 및 언로드 루틴도 드라이버에서 만든 시스템 스레드와 마찬가지로 PASSIVE_LEVEL 실행됩니다.
DISPATCH_LEVEL DISPATCH_LEVEL 및 APC_LEVEL 인터럽트는 StartIo 루틴에서 프로세서에서 마스킹됩니다.
AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (취소 스핀 잠금을 보유하는 동안) 및 CustomTimerDpc 루틴도 DpcForIsr 및 CustomDpc 루틴과 마찬가지로 DISPATCH_LEVEL 실행됩니다.
모든 인터럽트가 ISR 및 SynchCritSection 루틴에서 프로세서에서 마스킹된 드라이버 인터럽트 개체의 SynchronizeIrql보다 작거나 같은 디바이스 IRQL(DIRQL)
대부분의 상위 수준 드라이버는 두 개의 IRQL 중 하나에서 실행되는 동안 IRP를 처리합니다.
PASSIVE_LEVEL 드라이버의 디스패치 루틴에서 프로세서에서 마스킹된 인터럽트 없이
드라이버에서 만든 시스템 스레드 또는 작업자 스레드 콜백 루틴 또는 파일 시스템 드라이버와 마찬가지로 DriverEntry, Reinitialize, AddDevice 및 언로드 루틴도 PASSIVE_LEVEL 실행됩니다.
DISPATCH_LEVEL 드라이버의 IoCompletion 루틴에서 프로세서에서 마스킹된 DISPATCH_LEVEL 및 APC_LEVEL 인터럽트 사용
IoTimer, Cancel 및 CustomTimerDpc 루틴도 DISPATCH_LEVEL 실행됩니다.
경우에 따라 IRQL APC_LEVEL 대용량 스토리지 디바이스의 중간 및 최저 수준 드라이버가 호출됩니다. 특히 파일 시스템 드라이버가 드라이버를 낮추기 위해 IRP_MJ_READ 요청을 보내는 페이지 오류에서 발생할 수 있습니다.
대부분의 표준 드라이버 루틴은 단순히 적절한 지원 루틴을 호출할 수 있는 IRQL에서 실행됩니다. 예를 들어 디바이스 드라이버는 IRQL DISPATCH_LEVEL 실행하는 동안 AllocateAdapterChannel 을 호출해야 합니다. 대부분의 디바이스 드라이버는 StartIo 루틴에서 이러한 루틴을 호출하므로 일반적으로 이미 DISPATCH_LEVEL 실행 중입니다.
자체 IRP 큐를 설정하고 관리하기 때문에 StartIo 루틴이 없는 디바이스 드라이버가 AllocateAdapterChannel을 호출해야 할 때 반드시 DISPATCH_LEVEL IRQL에서 실행되는 것은 아닙니다. 이러한 드라이버는 AllocateAdapterChannel 을 호출할 때 필요한 IRQL에서 실행되고 호출 루틴이 제어를 다시 받을 때 원래 IRQL을 복원하도록 KeRaiseIrql 및 KeLowerIrql 호출 사이에 AllocateAdapterChannel 호출을 중첩해야 합니다.
드라이버 지원 루틴을 호출할 때는 다음 사항에 유의해야 합니다.
현재 IRQL보다 작은 입력 NewIrql 값으로 KeRaiseIrql을 호출하면 심각한 오류가 발생합니다. 원래 IRQL(즉, KeRaiseIrql 호출 후)을 복원하는 것을 제외하고 KeLowerIrql을 호출하면 치명적인 오류가 발생합니다.
IRQL >= DISPATCH_LEVEL 실행하는 동안 커널 정의 디스패처 개체에 대해 KeWaitForSingleObject 또는 KeWaitForMultipleObjects 를 호출하여 0이 아닌 간격을 기다리는 동안 치명적인 오류가 발생합니다.
이벤트, 세마포, 뮤텍스 또는 타이머가 신호 상태로 설정될 때까지 안전하게 기다릴 수 있는 유일한 드라이버 루틴은 드라이버에서 만든 스레드, DriverEntry 및 Reinitialize 루틴 또는 기본적으로 동기 I/O 작업(예: 대부분의 디바이스 I/O 제어 요청)에 대한 디스패치 루틴과 같은 IRQL PASSIVE_LEVEL 비비트 스레드 컨텍스트에서 실행되는 루틴입니다.
IRQL PASSIVE_LEVEL 실행하는 동안에도 호출 가능한 드라이버 코드는 입력 대기 매개 변수가 TRUE로 설정된 KeSetEvent, KeReleaseSemaphore 또는 KeReleaseMutex를 호출하지 않아야 합니다. 이러한 호출로 인해 페이지 오류가 발생할 수 있습니다.
IRQL보다 큰 APC_LEVEL 실행되는 루틴은 페이징된 풀에서 메모리를 할당하거나 페이징된 풀의 메모리에 안전하게 액세스할 수 없습니다. IRQL에서 실행되는 루틴이 APC_LEVEL 초과하면 페이지 오류가 발생하는 경우 심각한 오류입니다.
드라이버는 KeAcquireSpinLockAtDpcLevel 및 KeReleaseSpinLockFromDpcLevel을 호출할 때 IRQL DISPATCH_LEVEL 실행되어야 합니다.
드라이버는 KeAcquireSpinLock을 호출할 때 IRQL <= DISPATCH_LEVEL 실행할 수 있지만 KeReleaseSpinLock을 호출하여 해당 스핀 잠금을 해제해야 합니다. 즉, KeReleaseSpinLockFromDpcLevel을 호출하여 KeAcquireSpinLock으로 획득한 스핀 잠금을 해제하는 것은 프로그래밍 오류입니다.
드라이버가 IRQL > DISPATCH_LEVEL 실행하는 동안 KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock 또는 KeReleaseSpinLock을 호출해서는 안 됩니다.
ExInterlockedXxx 루틴과 같은 스핀 잠금을 사용하는 지원 루틴을 호출하면 호출자가 발생한 IRQL에서 아직 실행되고 있지 않은 경우 현재 프로세서에서 IRQL이 DISPATCH_LEVEL 또는 DIRQL로 발생합니다.
IRQL > PASSIVE_LEVEL 실행되는 드라이버 코드는 가능한 한 빨리 실행되어야 합니다. 루틴이 실행되는 IRQL이 높을수록 해당 루틴을 최대한 빨리 실행하도록 전체 성능이 양호하게 조정되는 것이 더 중요합니다. 예를 들어 KeRaiseIrql 을 호출하는 모든 드라이버는 가능한 한 빨리 KeLowerIrql 에 대한 상호 호출을 수행해야 합니다.
우선 순위 확인에 대한 자세한 내용은 예약, 스레드 컨텍스트 및 IRQL 백서를 참조하세요.