본문 바로가기
Programming/7. Device Driver

드라이버 개발 핵심 - 18화 인터럽트, ISR, DPC

by S.W 2026. 6. 8.
Windows 커널 & 드라이버 시리즈 — 18화

인터럽트, ISR, DPC — 하드웨어 신호를 두 단계로 처리하는 이유

📅 2026 ⏱ 읽기 약 14분 🏷 인터럽트 / ISR / DPC / IoConnectInterrupt
키보드를 누르면 어떻게 드라이버까지 신호가 전달될까요? 하드웨어는 CPU에 "인터럽트"를 보내고, CPU는 하던 일을 잠깐 멈추고 드라이버의 ISR을 호출해요. 그런데 ISR은 최대한 빨리 끝내야 하기 때문에, 무거운 처리는 DPC로 미뤄둡니다. 이 두 단계 처리 구조를 이번 화에서 파헤쳐 볼게요.

인터럽트란?

하드웨어 장치는 CPU에 비동기적으로 신호를 보낼 수 있어요. 이게 인터럽트입니다. CPU는 인터럽트를 받으면 현재 실행 중이던 코드를 잠깐 멈추고, 미리 등록된 인터럽트 핸들러(ISR)를 호출합니다. ISR이 끝나면 원래 코드로 돌아가요.

인터럽트를 사용하는 장치들:

  • 키보드, 마우스 — 키/버튼 입력 시
  • 네트워크 카드(NIC) — 패킷 수신 시
  • 하드 디스크/NVMe — I/O 완료 시
  • 타이머 — 주기적으로
  • USB 컨트롤러 — 장치 연결/데이터 수신 시

2단계 처리 구조 — ISR + DPC

ISR은 DIRQL이라는 매우 높은 IRQL에서 실행돼요. 이 레벨에서는 할 수 있는 게 극도로 제한되어 있어요(Paged Pool 접근 불가, 대기 불가, 심지어 DbgPrint도 안 됩니다). 그래서 ISR은 "하드웨어에서 데이터 읽기, 인터럽트 확인, DPC 큐잉" 세 가지만 하고 최대한 빨리 끝내요.

ISR (Interrupt Service Routine) — DIRQL에서 실행
역할: 인터럽트 확인, 데이터 빠르게 읽기, DPC 큐에 넣기, 인터럽트 해제
↓ KeInsertQueueDpc()
🔧
DPC (Deferred Procedure Call) — DISPATCH_LEVEL에서 실행
역할: 데이터 처리, IRP 완료, 추가 I/O 요청 — NonPaged Pool은 OK, 대기는 불가
↓ IoCompleteRequest()
📋
I/O 완료 루틴 / 앱 통지 — PASSIVE_LEVEL에서
앱의 비동기 I/O 완료 이벤트 발생, 콜백 호출 등

인터럽트 등록 — IoConnectInterruptEx

// ISR 함수 선언 BOOLEAN MyInterruptIsr( _In_ PKINTERRUPT Interrupt, _In_ PVOID ServiceContext) // AddDevice에서 넘긴 DeviceExtension 포인터 { PMY_DEV_EXT ext = (PMY_DEV_EXT)ServiceContext; // 1. 정말 내 장치의 인터럽트인가? (공유 인터럽트 환경에서 중요) if (!MyDevice_IsMyInterrupt(ext)) { return FALSE; // 내 것이 아님 → 다음 ISR에 기회 줌 } // 2. 하드웨어 레지스터에서 데이터 읽기 (빠르게!) ext->LastData = READ_PORT_UCHAR(ext->IoBase + DATA_REG); // 3. 인터럽트 해제 (ACK) WRITE_PORT_UCHAR(ext->IoBase + CTRL_REG, INT_ACK); // 4. DPC 큐에 넣기 KeInsertQueueDpc(&ext->Dpc, NULL, NULL); return TRUE; // 처리 완료 } // DriverEntry 또는 AddDevice에서 인터럽트 등록 IO_CONNECT_INTERRUPT_PARAMETERS params = {0}; params.Version = CONNECT_FULLY_SPECIFIED; params.FullySpecified.PhysicalDeviceObject = pdo; params.FullySpecified.InterruptObject = &ext->Interrupt; params.FullySpecified.ServiceRoutine = MyInterruptIsr; params.FullySpecified.ServiceContext = ext; params.FullySpecified.Vector = translatedVector; params.FullySpecified.Irql = translatedIrql; params.FullySpecified.SynchronizeIrql = translatedIrql; params.FullySpecified.InterruptMode = translatedMode; params.FullySpecified.ProcessorEnableMask = affinity; params.FullySpecified.ShareVector = shareVector; status = IoConnectInterruptEx(&params);

DPC 등록과 사용

// DPC 콜백 함수 VOID MyDpcRoutine( _In_ PKDPC Dpc, _In_ PVOID DeferredContext, // InitializeDpc에서 넘긴 컨텍스트 _In_ PVOID SystemArgument1, _In_ PVOID SystemArgument2) { PMY_DEV_EXT ext = (PMY_DEV_EXT)DeferredContext; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); // DISPATCH_LEVEL에서 실행 — NonPaged Pool OK, Paged Pool NO, 대기 NO ULONG data = ext->LastData; // IRP 큐에서 하나 꺼내서 완료 처리 PIRP pendingIrp = GetNextPendingIrp(ext); if (pendingIrp) { pendingIrp->IoStatus.Status = STATUS_SUCCESS; pendingIrp->IoStatus.Information = sizeof(ULONG); *(PULONG)pendingIrp->AssociatedIrp.SystemBuffer = data; IoCompleteRequest(pendingIrp, IO_DISK_INCREMENT); } } // DeviceExtension 초기화 시 DPC 등록 KeInitializeDpc( &ext->Dpc, // KDPC 구조체 MyDpcRoutine, // 콜백 함수 ext); // 컨텍스트 (DeferredContext로 전달됨)

ISR 작성 시 주의사항

  • 최대한 짧게 — ISR이 실행되는 동안 해당 IRQL 이하의 모든 인터럽트가 차단돼요. 길면 길수록 시스템 응답성이 나빠져요.
  • 재진입 가능(reentrant)해야 해요 — 멀티코어 시스템에서는 다른 코어의 인터럽트가 동시에 발생할 수 있어요.
  • 공유 인터럽트 확인 — PCI 환경에서는 여러 장치가 같은 IRQ를 공유할 수 있어요. ISR에서 "내 장치의 인터럽트인가?"를 반드시 확인하고, 아니면 FALSE를 반환해야 해요.
  • DbgPrint 사용 금지 — DIRQL에서는 DbgPrint가 내부적으로 락을 사용해서 데드락이 생길 수 있어요.
📌 DPC 타이머 — 주기적 작업 DPC를 일정 시간 후에, 또는 주기적으로 실행하고 싶다면 KTIMER + KDPC 조합을 쓰세요. KeInitializeTimer + KeSetTimerEx로 주기적 타이머를 설정하고, 만료 시 DPC를 실행하게 할 수 있어요. 폴링 대신 인터럽트를 쓰기 어려운 상황에서 유용합니다.

✅ 18화 요약 — 2개월차 완성!
  • 인터럽트 처리는 ISR(초단기, DIRQL)DPC(처리, DISPATCH_LEVEL) 두 단계로 나뉩니다.
  • ISR은 인터럽트 확인, 데이터 읽기, DPC 큐잉만 하고 바로 끝내야 해요.
  • DPC에서 실제 데이터 처리와 IRP 완료를 합니다.
  • IoConnectInterruptEx로 ISR을, KeInitializeDpc로 DPC를 등록합니다.
  • 공유 인터럽트 환경에서는 ISR에서 "내 장치 인터럽트인지" 반드시 확인하세요.
다음 화 — 3개월차 시작: 고급 기법으로!
19화 — WDF 프레임워크 소개: WDM과 무엇이 다른가 →

#인터럽트 #ISR #DPC #IoConnectInterrupt #DIRQL