QNX RTOS: 7-3. Handling Interrupts

2025. 12. 26. 15:50운영체제/QNX

QNX 하드웨어 인터럽트 처리 정리

1. 전체 흐름(그림 없이 텍스트로)

하드웨어가 인터럽트를 발생시키면 QNX에서는 대략 다음 순서로 처리된다.

  1. 인터럽트 소스(디바이스): UART, NIC, 디스크 I/O 완료, CAN 컨트롤러 등
  2. 인터럽트 컨트롤러(PIC):
    • ARM 계열: GIC(General Interrupt Controller)
    • x86_64: APIC(Advanced Programmable Interrupt Controller)
  3. CPU 코어에 인터럽트 전달QNX 마이크로커널이 트랩
  4. 커널이 인터럽트 번호를 식별하고 IST(Interrupt Service Thread) 를 깨움
  5. 깨우는 방식이 두 가지:
    • (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)”을 하여 하드웨어가 더 이상 인터럽트를 계속 어서트하지 않도록 해야 한다.

전형적인 루프:

  1. InterruptAttachThread()
  2. 무한 루프:
    • InterruptWait() 로 대기
    • 레지스터 읽기/쓰기 등 최소 처리(ack 포함)
    • 안전할 때 InterruptUnmask()
    • (필요 시) 후속 처리 또는 다른 스레드에 통지

6. Event 방식(InterruptAttachEvent)에서 pulse 기반 구조

 

Event 방식은 보통 pulse로 MsgReceive를 깨우는 패턴이 가장 흔하다.

구성 요소:

  • 채널(Channel) 생성 (pulse 수신용)
  • self-connection 생성(자기 자신에게 pulse 전달)
  • SIGEV_PULSE_INIT() 로 event 설정
    • PRIO_INHERIT 선택 가능
      • 드라이버 실행 우선순위(런치 우선순위)를 그대로 인터럽트 처리 우선순위로 사용하고 싶을 때 유용
    • pulse code로 “내 인터럽트 pulse”를 식별

런타임:

  • 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) 인터럽트 처리
  • 조건:
    1. attach 시 CPU_LOCAL flag 설정
    2. attach를 호출한 스레드는 runmask가 단일 코어여야 함
    3. 이후 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