QNX RTOS: 7-3. Handling Interrupts 실습
2025. 12. 26. 15:54ㆍ운영체제/QNX
실습 목표
- 키보드 인터럽트(Interrupt 1) 발생 시 동작하는 예제를 구현/확인한다.
- 두 가지 방식으로 인터럽트를 처리한다.
- IST(Interrupt Service Thread) 방식: InterruptAttachThread() + InterruptWait()
- Event/Pulse 방식: InterruptAttachEvent() + pulse 수신(MsgReceive())
1) IST 방식 (intsimple_kbd_ist.c)
1.1 처리 흐름
- InterruptAttachThread()로 인터럽트 서비스 스레드(IST) 등록
- 대상: Keyboard interrupt = 1
- 반환값: interrupt ID (이후 unmask 등에 사용)
- IST는 InterruptWait()로 블록(block) 상태에서 대기
- 사용자가 키를 누르면 인터럽트 발생 → IST가 깨어남(unblock)
- 인터럽트 재수신을 위해 InterruptUnmask(intr_id, ...) 수행
- printf() 수행
- 이번 실습에서는 counter를 추가해 “몇 번째 인터럽트인지” 출력
- 다시 루프로 돌아가 InterruptWait() 대기
1.2 관찰 포인트(데모 내용)
- 키 입력은 보통
- Key press 1회 인터럽트
- Key release 1회 인터럽트
→ 즉, 한 번 눌렀다 떼면 2번 출력되는 형태가 관찰될 수 있다.
- 키를 길게 누르면(hold)
- 반복 입력(auto-repeat) 으로 인터럽트가 다수 발생할 수 있다.
/*
* intsimple.c
*
* This module will contain code for handling an interrupt.
*
* To test it, simply run it. Note that you may have to do something
* to cause the interrupts to be generated (e.g. press a key if
* handling the keyboard interrupt).
*
* On an x86_64 box a good choices for the interrupt to use would be:
* 1 - keyboard
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/neutrino.h>
int main(int argc, char **argv)
{
int id;
int count = 0;
printf("starting...\n");
// register as an IST
id = InterruptAttachThread(1, 0);
if (id == -1) {
perror("InterruptAttachThread()");
exit(EXIT_FAILURE);
}
while (1) {
// block here waiting for the event
if (InterruptWait(_NTO_INTR_WAIT_FLAGS_FAST, NULL) == -1) {
perror("InterruptWait");
exit(EXIT_FAILURE);
}
// we got the interrupt, do any work and unmask it
if (InterruptUnmask(0, id) == -1) {
perror("InterruptUnmask");
}
// if using a high frequency interrupt, don't print every interrupt
printf("We got the event and unblocked, count = %d\n", count);
count++;
}
}
2) Event + Pulse 방식 (intsimple_kbd_ev.c)
2.1 왜 이 방식을 쓰나
- InterruptWait()로 전용 스레드를 “인터럽트만” 기다리게 하는 대신,
- pulse를 채널로 받아서 다음 같은 구조를 만들 수 있다.
- 멀티스레드 서버에서 여러 스레드가 동일 채널을 MsgReceive() 하며 처리 (pulse/메시지 분산)
- 인터럽트를 받은 스레드와 실제 처리를 수행하는 스레드를 분리 (pulse로 다른 스레드 깨우기)
- 단일 스레드 이벤트 루프에 클라이언트 메시지 + 인터럽트를 통합
2.2 설정 흐름(핵심 단계)
- 채널 생성: pulse를 받을 channel 생성
- self-connection 생성: 그 채널로 pulse가 들어오게 하려면 보통 자기 자신에게 ConnectAttach
- event 구조체 채움: SIGEV_PULSE_INIT 계열로
- 연결 ID
- 우선순위(예: PRIO_INHERIT 또는 명시 priority)
- pulse code(식별자)
- pulse value 등을 설정
- InterruptAttachEvent() 호출
- “인터럽트 1이 발생하면 이 pulse 이벤트를 deliver하라”라고 커널에 등록
2.3 런타임 처리 흐름
- MsgReceive()(또는 유사 루프)로 대기
- 키보드 인터럽트 발생 → pulse 수신 → 루프가 깨어남
- pulse의 code 검사
- 예제에서는 매크로로 설정한 값을 code에 넣어두고, 그 코드인지 확인
- InterruptUnmask() 수행
- printf() 수행(IST 예제와 동일한 목적)
- 다시 MsgReceive()로 대기
/*
* intsimple.c
*
* This module will contain code for handling an interrupt.
*
* To test it, simply run it. Note that you may have to do something
* to cause the interrupts to be generated (e.g. press a key if
* handling the keyboard interrupt).
*
* On an x86_64 box a good choices for the interrupt to use would be:
* 1 - keyboard
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/neutrino.h>
#define INT_PULSE_CODE (_PULSE_CODE_MINAVAIL+0)
#define INTERUPT_PRIORITY (15)
int main(int argc, char **argv)
{
int id, chid, self_coid;
int ret_val;
int count = 0;
struct _pulse msg;
struct sigevent int_event; // the event to wake up the thread
printf("starting...\n");
// create a channel for receiving the pulse message
chid = ChannelCreate(_NTO_CHF_PRIVATE);
if (chid == -1) {
perror("ChannelCreate");
exit(EXIT_FAILURE);
}
// create a connection to our channel so that the kernel will know where to
// send the pulse
self_coid = ConnectAttach(0, 0, chid, _NTO_SIDE_CHANNEL, 0);
if (self_coid == -1) {
perror("ConnectAttach");
exit(EXIT_FAILURE);
}
// set up an event for the kernel to use to wake up
// this thread. Use whatever type of event and event handling you want.
// In this case it's a pulse.
SIGEV_PULSE_INIT(&int_event, self_coid, INTERUPT_PRIORITY, INT_PULSE_CODE, 0);
// give the kernel the event
id = InterruptAttachEvent(1, &int_event, 0);
if (id == -1) {
perror("InterruptAttachEvent()");
exit(EXIT_FAILURE);
}
while (1) {
// block here waiting for the event
ret_val = MsgReceivePulse(chid, &msg, sizeof(msg), NULL);
if (ret_val == -1) {
perror("MsgReceive");
exit(EXIT_FAILURE);
}
if (msg.code == INT_PULSE_CODE) {
// we got the pulse from the interrupt, do any work and unmask it
if (InterruptUnmask(0, id) == -1) {
perror("InterruptUnmask");
}
// if using a high frequency interrupt, don't print every interrupt
printf("We got the event and unblocked, count = %d\n", count);
count++;
} else {
fprintf(stderr, "Got unexpected pulse with code: %d\n", msg.code);
}
}
}
3) IST vs Pulse(Event) 비교 결론
- IST(InterruptAttachThread)
- 장점: 구조 단순, 낮은 오버헤드, 빠른 반응
- 단점: 스레드가 특정 인터럽트에 전용으로 묶임(통합 루프 어려움)
- Pulse(Event)(InterruptAttachEvent)
- 장점: 메시지 루프/스레드풀과 자연스럽게 통합, 구조 유연
- 단점: pulse 전달/큐잉 등으로 오버헤드가 조금 더 생길 수 있음
'운영체제 > QNX' 카테고리의 다른 글
| QNX RTOS: 8-2. Getting and Setting the System Clock (0) | 2026.01.02 |
|---|---|
| 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 |