임베디드 면접 용어 사전
인터럽트인터럽트 서비스 루틴

ISR

인터럽트 발생 시 실행되는 짧은 처리 루틴. 긴 작업은 플래그·큐로 태스크에 미루는 것이 원칙이다.

인터럽트가 발생했을 때 실행되는 함수(Interrupt Service Routine).

작성 원칙 — 딱 하나로 요약하면

최대한 짧게. 나머지는 메인 루프로 미룬다.

— ISR이 실행되는 동안

  • ① 같거나 낮은 우선순위 인터럽트가 전부 대기한다
  • ② 메인 루프가 멈춘다
  • ③ 실시간 요구를 어길 수 있다

ISR에서 하지 말아야 할 것

금지이유
printf / sprintf매우 느리고(수백 μs) 재진입 불가
malloc / free힙 자료구조를 건드림. 재진입 불가
긴 루프·복잡한 계산다른 인터럽트를 굶긴다
블로킹 대기while (!flag) — 영영 못 나올 수 있다
RTOS 블로킹 APIxQueueSend가 아니라 xQueueSendFromISR
부동소수점 (FPU 없으면)소프트웨어 에뮬레이션으로 수백 사이클

printf 디버깅이 ISR에서 특히 위험하다 — 타이밍이 완전히 바뀌어 원래 버그가 사라지거나 새 버그가 생긴다(하이젠버그).

표준 패턴 — deferred 처리

volatile uint8_t data_ready = 0;
volatile uint8_t rx_buf[64];

void USART1_IRQHandler(void) {
    if (USART1->SR & USART_SR_RXNE) {
        rx_buf[idx++] = USART1->DR;      // 읽기만 (읽으면 플래그도 클리어)
        if (idx >= 64) data_ready = 1;
    }
}

int main(void) {
    while (1) {
        if (data_ready) {
            data_ready = 0;
            parse_packet(rx_buf);         // 오래 걸려도 안전
        }
    }
}

반드시 지킬 것

① 공유 변수에 volatile

volatile uint8_t flag;    // 없으면 메인 루프가 무한 대기에 빠진다

② 플래그 클리어

void EXTI0_IRQHandler(void) {
    handle();
    EXTI->PR = EXTI_PR_PR0;    // 안 지우면 무한 재진입
}

어떤 플래그는 데이터 레지스터를 읽는 것으로 자동 클리어된다 (UART의 RXNE). 데이터시트를 확인해야 한다.

③ 여러 바이트 공유 데이터는 원자성 확보

// 메인 루프에서
__disable_irq();
uint32_t snapshot = shared_counter;    // 32비트 MCU면 사실 원자적이지만
__enable_irq();                         // 구조체·64비트는 반드시 보호

④ 재진입 가능한 함수만 호출

static 지역 변수를 쓰는 함수를 ISR과 메인이 함께 부르면 데이터가 깨진다.

RTOS 환경에서

void USART1_IRQHandler(void) {
    BaseType_t woken = pdFALSE;
    xQueueSendFromISR(queue, &data, &woken);   // FromISR 버전 필수
    portYIELD_FROM_ISR(woken);                  // 더 높은 태스크가 깨면 즉시 전환
}

FromISR 접미사가 붙은 API만 사용해야 한다. 일반 버전은 블로킹할 수 있는데, ISR에서 블로킹하면 시스템이 멈춘다.

우선순위 제약 — FreeRTOS에서 RTOS API를 부르는 ISR은 configMAX_SYSCALL_INTERRUPT_PRIORITY보다 낮거나 같은 우선순위여야 한다. 더 높은 ISR에서 API를 부르면 커널의 임계 구역 보호가 무력화되어 간헐적으로 리스트가 깨지는 최악의 버그가 된다.

공유 하드웨어 인터럽트

void EXTI9_5_IRQHandler(void) {        // 5~9번 핀이 같은 벡터를 공유
    if (EXTI->PR & EXTI_PR_PR5) { handle5(); EXTI->PR = EXTI_PR_PR5; }
    if (EXTI->PR & EXTI_PR_PR7) { handle7(); EXTI->PR = EXTI_PR_PR7; }
}

어느 소스인지 판별하고 해당 플래그만 지워야 한다. 전부 지우면 아직 처리 안 한 다른 원인을 잃는다.

ISR 실행 시간 측정

void TIM2_IRQHandler(void) {
    GPIOA->BSRR = DEBUG_PIN;
    // …
    GPIOA->BSRR = DEBUG_PIN << 16;
    TIM2->SR &= ~TIM_SR_UIF;
}

오실로스코프의 펄스 폭이 실행 시간이다. 이 값이 인터럽트 주기의 몇 %인지가 CPU 부하의 직접적 지표가 된다. 50%를 넘으면 설계를 다시 봐야 한다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 인터럽트 — 벡터·우선순위·latency·재진입·ISR 금기·디바운싱