인터럽트 — 임베디드 면접 (실제 기출 기반·심화)
"인터럽트랑 폴링 차이", "인터럽트 종류", "ISR 안에서 하면 안 되는 것", "우선순위·중첩", "ISR과 메인이 공유하는 변수에 왜 volatile", "버튼 채터링 어떻게 잡나" — 실시간성·동시성 감각을 보는 핵심 단골. ISR은 임베디드 버그의 절반이 모이는 곳이다.
면접관은 "ISR을 짧게 써야 한다"는 답에서 멈추지 않고 "왜? 길면 정확히 무슨 일이 나나?", **"volatile만으로 충분한가?"**로 파고든다.
1. 인터럽트 vs 폴링 (★ 단골)
실제 질문: "인터럽트와 폴링의 차이는?" (gyoogle Interrupt·velog @verilog Q70)
| 인터럽트 | 폴링 | |
|---|---|---|
| 방식 | 이벤트 발생 시 HW가 알림 | CPU가 계속 상태를 확인 |
| CPU | 평소 다른 일·저전력(WFI) 가능 | 바쁜 대기(낭비) |
| 응답성 | 빠름·결정적 | 폴링 주기에 의존 |
| 복잡도 | 복잡(동시성·race) | 단순 |
깊이 — 폴링이 유리할 때(★ 함정 깨기): 보통 인터럽트가 효율적이지만, 이벤트가 폴링 1주기보다 촘촘하게 몰아치면 인터럽트 진입/복귀 오버헤드·지터가 누적돼 고속 폴링이 유리하기도(고속 통신 비지웨이트). "인터럽트가 항상 옳다"는 오답.
꼬리질문: "저전력에서 인터럽트의 이점?" → CPU를 WFI/sleep에 두고 인터럽트로 깨움(배터리). / "폴링으로 버튼을 읽으면?" → 주기 사이 이벤트를 놓치거나 채터링.
2. 인터럽트 종류·처리 과정 (★ 단골)
실제 질문: "인터럽트 종류는?" · "인터럽트 처리 과정을 설명하라" (velog @verilog·gyoogle)
- 외부(하드웨어) 인터럽트: 핀·주변장치 신호(GPIO·UART RX·타이머).
- 내부/예외(Trap·Fault): 0 나누기·잘못된 메모리 접근·HardFault 등 실행 중 발생.
- 소프트웨어 인터럽트:
SVC(시스템 콜·RTOS 진입)·PendSV(컨텍스트 스위칭).
처리 과정: ① 현재 명령 마무리 → ② 8개 레지스터 자동 스태킹(03편) → ③ IPSR에 예외번호·벡터 테이블에서 핸들러 주소 로드 → ④ ISR 실행 → ⑤ EXC_RETURN으로 복원·복귀. 원인 검색이 필요 없는 벡터 인터럽트라 빠르다.
꼬리질문: "0으로 나누면 어떻게 되나?" → 설정에 따라 UsageFault(DIV_0_TRP) 또는 0 반환. / "SVC와 일반 인터럽트 차이?" → SVC는 소프트웨어가 의도적으로 호출(시스템 콜).
3. NVIC·우선순위·선점·중첩 (★★ 단골)
실제 질문: "인터럽트 우선순위를 설정할 수 있는 이유는?" · "인터럽트가 중첩(nesting)되려면?" (velog @verilog Q73·Q138)
- NVIC: 최대 240 소스·256 우선순위·자동 스태킹.
- 우선순위 숫자가 작을수록 높음(0이 최고). NMI·HardFault는 고정 최상위.
- 선점(preemption): 높은 우선순위 인터럽트가 낮은 ISR 실행 중에 끼어듦 → 중첩 인터럽트.
- 우선순위 그룹핑: 우선순위를 선점 우선순위 + 서브 우선순위로 나눔. 선점 우선순위가 같으면 서로 선점 못 함(서브는 동시 pending 시 순서만).
깊이 — 중첩 조건: 들어온 인터럽트가 현재 실행 중인 ISR보다 선점 우선순위가 높아야 중첩된다. (BASEPRI로 일정 우선순위 이하 중첩을 막을 수 있다.)
함정: ❌ "우선순위 숫자가 클수록 높다" → 작을수록 높다.
4. 지연·테일체이닝·late-arrival (★ 심화·재검증)
- 인터럽트 지연(latency): 발생→ISR 첫 명령까지. Cortex-M3/M4는 12사이클(제로 wait-state)·하드웨어 자동 스태킹 덕에 결정적.
- 테일체이닝(tail-chaining): ISR이 끝나는데 다음 인터럽트가 이미 pending이면 pop→push를 생략하고 바로 다음 ISR로 → 6사이클(총 18사이클 절약).
- late-arrival: 예외 진입(스태킹) 중 더 높은 우선순위가 오면 그쪽을 먼저 처리하고, 끝나면 원래 가려던 예외로 테일체이닝 → 세 최적화가 맞물린다.
깊이: 이 셋이 Cortex-M이 짧은 인터럽트를 저지연·저지터로 처리하는 비결. 면접에서 "왜 Cortex-M 인터럽트가 빠른가"의 답.
꼬리질문: "지연을 줄이려면?" → ISR 짧게·임계구역(인터럽트 disable) 최소화·우선순위 적절히.
5. ISR 작성 규칙 — "짧게, 미루기" (★★★ 최빈출)
실제 질문: "ISR 안에서 긴 연산을 수행하면 어떻게 되나?" · "ISR에서 절대 하면 안 되는 것은?" (velog @verilog Q75·nhtranngoc Q2·@embeddedjune)
ISR은 최소한만 하고 무거운 일은 메인/태스크로 미룬다(top-half/bottom-half, deferred interrupt processing).
ISR에서 하지 말 것:
- 오래 걸리는 작업·루프 → 다른 인터럽트·실시간성 방해(우선순위 같은/낮은 인터럽트가 막힘).
- 블로킹 → 세마포어 대기(take)·
delay()·폴링 대기는 절대 금지. malloc/free(비결정적·재진입 불가)·printf(느림·재진입·블로킹).- 우선순위가 자기보다 낮은 것에 의존(데드락).
대신:
- 플래그만 세팅·데이터를 큐에 넣고 빠져나오기. 메인 루프나 RTOS 태스크가 처리.
- RTOS면 ISR-safe API로 세마포어 give·큐 send(
...FromISR) → 태스크 깨우기. 깨운 태스크가 더 높으면portYIELD_FROM_ISR(&xHigherPriorityTaskWoken)로 즉시 스케줄(RTOS 편).
꼬리질문: "ISR이 길면 정확히 무슨 일?" → 같은/낮은 우선순위 인터럽트가 지연·유실(pending이 덮임)·실시간 데드라인 미스. / "왜 malloc 금지?" → 힙 락·재진입 불가·비결정적 지연.
함정: ❌ "ISR에서 xSemaphoreTake로 기다리면 된다" → 블로킹·데드락. give만(FromISR).
6. 공유 데이터·race·재진입 (★★ 단골)
실제 질문: "ISR과 메인이 공유하는 변수에 왜
volatile을 쓰나?" · "재진입(reentrant) 함수란?" (lunakimvision·nhtranngoc Q2)
ISR과 메인(또는 다른 ISR)이 같은 변수를 만지면 race:
volatile(C 편): 컴파일러가 캐싱·최적화 못 하게 → 매번 메모리에서 읽기.- ⚠️ 하지만 volatile은 원자성이 아니다(★ 핵심 함정):
flag++(읽기-수정-쓰기 3단계)·멀티바이트 변수는 ISR과 겹치면 찢김(tearing). → 임계구역(PRIMASK/BASEPRI로 인터럽트 잠깐 disable) 또는 원자 연산으로 보호. - 재진입(reentrancy): ISR과 메인이 같은 함수를 부르면, 그 함수가 전역/static 상태를 안 쓰고 재진입 안전해야.
strtok·정적 버퍼 함수는 위험. - 단일 플래그(
volatile uint8_t, 정렬된 워드폭 이내)는 보통 원자적 쓰기지만, 여러 플래그를 묶은 비트필드는 읽기-수정-쓰기라 위험 → 비트밴딩(03편)·마스킹.
꼬리질문: "volatile만으로 충분한 경우?" → 단일 워드 읽기/쓰기·플래그(찢김 없음). 읽기-수정-쓰기는 불충분. / "16비트 MCU에서 32비트 카운터 공유?" → 비원자 읽기 찢김 → 임계구역으로 읽기.
함정: ❌ "volatile = thread-safe/atomic" → 틀림(가시성만, 동기화 아님).
7. 디바운싱 — 채터링 잡기 (★ 단골·실무)
기계식 스위치는 누를 때 접점이 수 ms간 튀어(채터링) 한 번 눌러도 여러 인터럽트가 뜬다 → 디바운싱:
- SW: 에지 인터럽트에서 타이머 시작, N ms 뒤 상태 재확인해 안정됐으면 인정. 또는 주기 샘플링 후 연속 동일 N회면 확정.
- HW: RC 필터(저항+커패시터)·슈미트 트리거로 물리적으로 완만하게.
- ⚠️ ISR에서
delay로 기다리는 건 금지(5절) → 타이머/플래그로 비차단.
꼬리질문: "디바운싱을 ISR에서 어떻게?" → 첫 에지에서 그 인터럽트를 잠깐 막고(또는 플래그) 타이머 만료 시 재확인 → 다시 활성.
한국 임베디드 면접 단골 Q&A (답변 골격)
| 질문 | 핵심 답 |
|---|---|
| 인터럽트 vs 폴링 | 이벤트 알림(효율·복잡) vs 계속 확인(단순·낭비)·빈번 짧으면 폴링도 |
| 인터럽트 종류 | 외부(HW)·내부(Trap/Fault)·소프트웨어(SVC/PendSV) |
| 처리 과정 | 자동 스태킹→벡터 점프→ISR→EXC_RETURN |
| NVIC·우선순위 | 240소스·256우선순위·숫자 작을수록 높음·선점/서브 |
| 중첩 조건 | 현재보다 선점 우선순위 높아야·BASEPRI로 제한 |
| 지연·테일체이닝 | 12사이클·테일체이닝 6·late-arrival |
| ISR 금기 | 블로킹·malloc·printf·긴 작업·낮은 우선순위 대기 |
| ISR 설계 | top-half 플래그/큐만, 무거운 건 태스크로(deferred) |
| 공유 변수 volatile | 캐싱 금지·매번 메모리·단 원자성 아님 |
| race 보호 | 임계구역(PRIMASK/BASEPRI)·원자 연산 |
| 재진입 함수 | 전역/static 없는·재진입 함수만 호출 |
| 디바운싱 | SW(타이머/샘플링)·HW(RC/슈미트)·ISR에서 delay 금지 |
꼬리질문 대비 (상 난이도)
- "ISR에서 세마포어를 기다리면?" → 블로킹 → 데드락·우선순위 역전 → give만(FromISR), wait는 태스크에서.
- "volatile인데
count++가 깨진다?" → 비원자 읽기-수정-쓰기 → 인터럽트 막고(임계구역) 처리. - "인터럽트를 놓친다?" → ISR이 너무 길거나 같은 우선순위가 막힘·pending이 덮임 → ISR 짧게·우선순위 재설계.
- "임계구역은 PRIMASK? BASEPRI?" → PRIMASK는 모든 인터럽트 막아 지연↑ → BASEPRI로 낮은 우선순위만.
- "디바운싱을 ISR에서 delay로?" → 인터럽트 막힘 → 타이머 기반 비차단.
한 줄 요약 — 인터럽트(이벤트·효율)↔폴링(단순·빈번 짧으면 유리). 종류는 외부·내부 Trap·소프트웨어(SVC). NVIC(240소스·256우선순위·숫자 작을수록 높음·선점/서브), 지연 12사이클·테일체이닝 6·late-arrival. ISR은 짧게(플래그/큐만)·블로킹·malloc·printf 금지·무거운 건 태스크로(deferred). 공유 변수는 volatile + 임계구역(volatile은 원자 아님)·재진입 안전. 스위치 채터링은 디바운싱(타이머/RC, ISR delay 금지).
(출처 — 한국 면접 기출·1차자료 교차검증 2026-06: gyoogle Interrupt·velog @verilog Firmware 직무적성(Q70·73·75·138)·nhtranngoc embedded-interview Q2·lunakimvision volatile · ARM — Cortex-M 인터럽트 지연(12사이클·테일체이닝)·Memfault NVIC/Exception·ARMv7-M ARM 교차검증.)