QNX RTOS: 7-3. Handling Interrupts 실습

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

실습 목표

  • 키보드 인터럽트(Interrupt 1) 발생 시 동작하는 예제를 구현/확인한다.
  • 두 가지 방식으로 인터럽트를 처리한다.
    1. IST(Interrupt Service Thread) 방식: InterruptAttachThread() + InterruptWait()
    2. Event/Pulse 방식: InterruptAttachEvent() + pulse 수신(MsgReceive())

 

1) IST 방식 (intsimple_kbd_ist.c)

1.1 처리 흐름

  1. InterruptAttachThread()로 인터럽트 서비스 스레드(IST) 등록
    • 대상: Keyboard interrupt = 1
    • 반환값: interrupt ID (이후 unmask 등에 사용)
  2. IST는 InterruptWait()로 블록(block) 상태에서 대기
  3. 사용자가 키를 누르면 인터럽트 발생 → IST가 깨어남(unblock)
  4. 인터럽트 재수신을 위해 InterruptUnmask(intr_id, ...) 수행
  5. printf() 수행
    • 이번 실습에서는 counter를 추가해 “몇 번째 인터럽트인지” 출력
  6. 다시 루프로 돌아가 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 설정 흐름(핵심 단계)

  1. 채널 생성: pulse를 받을 channel 생성
  2. self-connection 생성: 그 채널로 pulse가 들어오게 하려면 보통 자기 자신에게 ConnectAttach
  3. event 구조체 채움: SIGEV_PULSE_INIT 계열로
    • 연결 ID
    • 우선순위(예: PRIO_INHERIT 또는 명시 priority)
    • pulse code(식별자)
    • pulse value 등을 설정
  4. InterruptAttachEvent() 호출
    • “인터럽트 1이 발생하면 이 pulse 이벤트를 deliver하라”라고 커널에 등록

2.3 런타임 처리 흐름

  1. MsgReceive()(또는 유사 루프)로 대기
  2. 키보드 인터럽트 발생 → pulse 수신 → 루프가 깨어남
  3. pulse의 code 검사
    • 예제에서는 매크로로 설정한 값을 code에 넣어두고, 그 코드인지 확인
  4. InterruptUnmask() 수행
  5. printf() 수행(IST 예제와 동일한 목적)
  6. 다시 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 전달/큐잉 등으로 오버헤드가 조금 더 생길 수 있음