QNX RTOS: 7-3. Handling Interrupts
2025. 12. 26. 15:50ㆍ운영체제/QNX
QNX 하드웨어 인터럽트 처리 정리
1. 전체 흐름(그림 없이 텍스트로)
하드웨어가 인터럽트를 발생시키면 QNX에서는 대략 다음 순서로 처리된다.
- 인터럽트 소스(디바이스): UART, NIC, 디스크 I/O 완료, CAN 컨트롤러 등
- 인터럽트 컨트롤러(PIC):
- ARM 계열: GIC(General Interrupt Controller)
- x86_64: APIC(Advanced Programmable Interrupt Controller)
- CPU 코어에 인터럽트 전달 → QNX 마이크로커널이 트랩
- 커널이 인터럽트 번호를 식별하고 IST(Interrupt Service Thread) 를 깨움
- 깨우는 방식이 두 가지:
- (A) 드라이버가 직접 IST를 가짐: 드라이버의 IST 스레드가 바로 깨어남
- (B) 이벤트(Event)로 통지: 커널 내부 IST(고우선순위)가 먼저 깨어서 이벤트(예: pulse)를 드라이버로 전달

핵심: IST도 “특수한 커널 객체”가 아니라 스케줄링되는 일반 스레드다.
단, 커널 내부 IST는 우선순위 253의 예약(high priority) 스레드로 동작한다.
2. 선택지 2개: Thread 방식 vs Event 방식
2.1 InterruptAttachThread (드라이버 스레드가 IST가 됨)
- 의미: “내 스레드를 인터럽트 전용 스레드(IST)로 만들겠다.”
- 인터럽트 발생 시 커널이 그 스레드를 직접 깨움
- 이후 IST는 InterruptWait()로 대기한다.
장점
- 가장 낮은 지연(latency): 인터럽트 발생 → 내 IST가 즉시 깨어남
- 구조가 단순(전용 루프)
단점
- 스레드가 “오직 그 인터럽트만” 기다릴 수 있음
- 인터럽트가 여러 개면 인터럽트당 IST 스레드가 추가로 필요
- 단일 스레드로 “클라이언트 메시지 처리 + 인터럽트 처리”를 같이 하기는 어려움
2.2 InterruptAttachEvent (이벤트로 통지 받음)
- 의미: “내가 IST가 되긴 싫고, struct sigevent 형태로 통지해줘.”
- 커널이 커널 내부 IST를 깨워서, 지정한 이벤트를 드라이버에 전달한다.
- 드라이버는 보통 MsgReceive() 루프에서 pulse를 받아 처리한다.
장점
- 유연함:
- “하나의 메시지 루프”에서 클라이언트 요청 + 하드웨어 이벤트를 같이 처리 가능
- 진짜 단일 스레드(Resource Manager 스타일) 아키텍처가 가능해짐
- pulse/sem 등 큐잉되는 이벤트를 활용 가능(구조적으로 안정적)
단점
- Thread 직접 방식보다 약간의 오버헤드(커널 IST → 이벤트 전달 2단계)
3. 핵심 API 목록과 의미

- InterruptAttachThread() : 호출한 스레드를 IST로 등록
- InterruptWait() : IST가 인터럽트 올 때까지 블록(대기)
- InterruptAttachEvent() : 인터럽트 발생 시 이벤트(struct sigevent) 전달
- InterruptDetach() : 등록 해제
- InterruptMask() / InterruptUnmask() : 해당 인터럽트만 마스크/언마스크
주의: Disable/Enable
- InterruptDisable() / InterruptEnable() 은 “해당 CPU 코어로 들어오는 모든 인터럽트”를 끄고 켜는 성격
- 스케줄링/커널 동작까지 광범위하게 영향 → 일반 드라이버에서는 거의 쓰지 않음
4. 권한(Privilege) 포인트
- InterruptAttachThread / InterruptAttachEvent
→ PROCMGR_AID_INTERRUPT 필요(특정 interrupt ID에 대한 권한 포함) - InterruptDisable / InterruptEnable
→ I/O privilege 필요
→ 보통 ThreadCtl()로 I/O privilege 설정이 선행되어야 함
→ 그러나 일반적인 인터럽트 처리 흐름에서는 거의 불필요
5. Driver A (InterruptAttachThread) 기본 구조

커널은 인터럽트를 받으면 언마스크 전에 먼저 마스크하고 IST를 깨운다.
IST는 최소한 “인터럽트 확인(ack)”을 하여 하드웨어가 더 이상 인터럽트를 계속 어서트하지 않도록 해야 한다.
전형적인 루프:
- InterruptAttachThread()
- 무한 루프:
- InterruptWait() 로 대기
- 레지스터 읽기/쓰기 등 최소 처리(ack 포함)
- 안전할 때 InterruptUnmask()
- (필요 시) 후속 처리 또는 다른 스레드에 통지
6. Event 방식(InterruptAttachEvent)에서 pulse 기반 구조

Event 방식은 보통 pulse로 MsgReceive를 깨우는 패턴이 가장 흔하다.
구성 요소:
- 채널(Channel) 생성 (pulse 수신용)
- self-connection 생성(자기 자신에게 pulse 전달)
- SIGEV_PULSE_INIT() 로 event 설정
- PRIO_INHERIT 선택 가능
- 드라이버 실행 우선순위(런치 우선순위)를 그대로 인터럽트 처리 우선순위로 사용하고 싶을 때 유용
- pulse code로 “내 인터럽트 pulse”를 식별
- PRIO_INHERIT 선택 가능
런타임:
- InterruptAttachEvent(intr, &event, flags)
- 메시지 루프에서 MsgReceive()
- rcvid == 0 이면 pulse
- pulse code 확인 후 인터럽트 처리 + InterruptUnmask()

7. 자주 쓰는 flags
7.1 _NTO_INTR_FLAGS_NO_UNMASK
- attach 시 커널이 자동으로 unmask하는데,
- 아직 준비가 덜 됐으면(초기화/레지스터 세팅 전) 자동 unmask를 막고
- 준비 완료 시점에 직접 InterruptUnmask() 하는 패턴
7.2 _NTO_INTR_FLAGS_CPU_LOCAL
- 코어별(local) 인터럽트 처리
- 조건:
- attach 시 CPU_LOCAL flag 설정
- attach를 호출한 스레드는 runmask가 단일 코어여야 함
- 이후 Unmask 등 처리도 그 코어에 계속 고정되어야 함
(다른 코어로 이동하면 “다른 하드웨어”를 unmask하는 문제가 생김)
8. 어떤 방식을 언제 고르나
InterruptAttachThread(IST 직접) 추천 상황
- 최저 latency가 중요
- 인터럽트 처리 전용 스레드를 두는 게 자연스러운 드라이버 구조
- 인터럽트 종류가 많지 않거나, 인터럽트당 스레드 분리가 수용 가능
InterruptAttachEvent(pulse 등) 추천 상황
- 단일 스레드로 클라이언트 메시지 + 인터럽트를 함께 처리하고 싶음
- thread pool/메시지 루프 기반(resource manager 스타일)
- 큐잉(누적)되는 통지가 유리한 경우(예: pulse/sem 기반)
대체로 “전용 IST(InterruptAttachThread)” 또는 “pulse 기반 Event(InterruptAttachEvent + SIGEV_PULSE)”를 가장 많이 쓴다.
'운영체제 > QNX' 카테고리의 다른 글
| QNX RTOS: 8-1. Timing Architecture (1) | 2026.01.02 |
|---|---|
| QNX RTOS: 7-3. Handling Interrupts 실습 (0) | 2025.12.26 |
| QNX RTOS: 7-2. Programming PCI Bus Devices (0) | 2025.12.26 |
| QNX RTOS: 7-1. Hardware I/O (0) | 2025.12.26 |
| QNX RTOS: 6-2. IPC 선택 기준 (0) | 2025.12.15 |