IRQL 완전 정복 — Interrupt Request Level의 모든 것
IRQL이란?
IRQL(Interrupt Request Level)은 CPU 코어별로 현재의 "인터럽트 처리 우선순위"를 나타내는 값이에요. 현재 IRQL보다 낮거나 같은 수준의 인터럽트는 현재 코드가 실행되는 동안 차단됩니다.
쉽게 비유하면, 병원 응급실의 트리아지(우선순위 분류)와 비슷해요. 외래 환자(낮은 IRQL)는 응급 환자(높은 IRQL)가 처리되는 동안 대기해야 해요.
핵심 규칙 — 반드시 외워야 해요
- Paged Pool 접근 → 페이지 폴트 → BSOD
- 대기 함수 호출 (KeWaitForSingleObject 등, 0 타임아웃 제외) → 데드락 또는 BSOD
- 유저 모드 메모리 접근 → 페이지 폴트 → BSOD
- sleep/delay 함수 → 스케줄러 동작 불가 → 시스템 멈춤
왜 Paged Pool 접근이 불가능할까요? DISPATCH_LEVEL 이상에서는 스케줄러가 동작하지 않아요. 즉, 이 코드가 실행되는 동안 CPU는 절대 다른 스레드로 전환되지 않아요. 그런데 페이지 폴트가 나면 해당 페이지를 디스크에서 읽어와야 하는데, 이 작업은 I/O 완료를 기다려야 하고, 기다리려면 스케줄러가 다른 스레드로 전환해야 해요. 근데 스케줄러가 막혀있으니 → 영원히 기다림 → BSOD입니다.
IRQL 확인하고 변경하기
// 현재 IRQL 확인 KIRQL currentIrql = KeGetCurrentIrql(); if (currentIrql > DISPATCH_LEVEL) { // 여기서 Paged Pool 접근하면 죽음 } // IRQL 올리기 (스핀락 획득 시 자동으로 올라가지만, 수동으로도 가능) KIRQL oldIrql; KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); // DISPATCH_LEVEL로 올림 // ... 이 사이에서는 스케줄러가 동작하지 않음 (짧게 유지해야!) KeLowerIrql(oldIrql); // 원래 IRQL로 복귀DispatchRoutine의 IRQL
드라이버의 MajorFunction 핸들러(DispatchRead, DispatchIoControl 등)는 일반적으로 PASSIVE_LEVEL 또는 APC_LEVEL에서 호출돼요. 그래서 Paged Pool 접근, 대기 함수 사용이 모두 가능합니다.
반면 DPC 루틴과 완료 루틴(CompletionRoutine)은 DISPATCH_LEVEL에서 호출될 수 있어요. ISR(인터럽트 서비스 루틴)은 DIRQL에서 실행됩니다.
| 코드 위치 | IRQL | Paged Pool | Wait 함수 |
|---|---|---|---|
| DriverEntry, DriverUnload | PASSIVE_LEVEL | 가능 | 가능 |
| MajorFunction (Dispatch 루틴) | PASSIVE 또는 APC | 가능 | 가능 |
| DPC 루틴 | DISPATCH_LEVEL | 불가! | 불가! |
| ISR (인터럽트 서비스 루틴) | DIRQL | 불가! | 불가! |
| 완료 루틴 (CompletionRoutine) | DISPATCH_LEVEL 이하 | 조건부 | 조건부 |
IRQL 관련 BSOD — 디버깅 힌트
IRQL 관련 BSOD의 대표 코드들이에요. 24화(BSOD 분석)에서 자세히 다루지만, 미리 알아두면 좋아요:
- IRQL_NOT_LESS_OR_EQUAL (0x0000000A) — 높은 IRQL에서 페이지 가능 주소 접근
- DRIVER_IRQL_NOT_LESS_OR_EQUAL (0x000000D1) — 드라이버가 높은 IRQL에서 잘못된 메모리 접근
- SPIN_LOCK_ALREADY_OWNED (0x00000F) — 같은 CPU에서 스핀락을 이미 가지고 있는 상태에서 다시 획득 시도
실수를 줄이는 코딩 습관
// SAL 어노테이션으로 IRQL 요구사항 문서화 _IRQL_requires_max_(DISPATCH_LEVEL) VOID MyFunction(PVOID Param) { // DISPATCH_LEVEL 이하에서 호출되어야 함을 명시 // Visual Studio의 코드 분석 도구가 이 어노테이션을 체크해줘요! } _IRQL_requires_(PASSIVE_LEVEL) NTSTATUS MyBlockingOperation(VOID) { // 반드시 PASSIVE_LEVEL에서 호출되어야 함 KeWaitForSingleObject(...); // 여기선 OK }SAL(Source Annotation Language) 어노테이션을 함수 선언에 붙여두면, Visual Studio의 정적 분석 도구(Code Analysis)가 잘못된 IRQL에서 호출하는 코드를 컴파일 타임에 경고해줘요. 컴파일할 때 잡히는 버그가 제일 좋은 버그입니다.
- IRQL은 CPU 코어의 현재 인터럽트 처리 우선순위예요. 높을수록 더 많은 인터럽트를 차단합니다.
- 주요 레벨: PASSIVE(0) → APC(1) → DISPATCH(2) → DIRQL(3~26)
- DISPATCH_LEVEL 이상에서는 Paged Pool 접근, 대기 함수 호출이 절대 불가합니다.
- MajorFunction 핸들러는 PASSIVE/APC, DPC와 ISR은 DISPATCH/DIRQL에서 실행됩니다.
- SAL 어노테이션으로 IRQL 요구사항을 문서화하면 정적 분석 도구가 도와줍니다.
#IRQL #DISPATCH_LEVEL #PASSIVE_LEVEL #커널동기화 #BSOD원인
'Programming > 7. Device Driver' 카테고리의 다른 글
| 드라이버 개발 핵심 - 16화 커널 메모리 풀 관리 (0) | 2026.06.04 |
|---|---|
| 드라이버 개발 핵심 - 15화 스핀락과 동기화 기법 (0) | 2026.06.03 |
| 드라이버 개발 핵심 - 13화 IRP 디스패치 루틴 구현 (0) | 2026.06.01 |
| 드라이버 개발 핵심 - 11화 디바이스 오브젝트와 디바이스 스택 (0) | 2026.05.29 |
| 드라이버 개발 핵심 - 10화 DriverEntry와 드라이버 오브젝트 (0) | 2026.05.28 |