인터럽트 서비스 루틴 (ISR, Interrupt Service Routine)
핵심 인사이트 (3줄 요약)
- 본질: 인터럽트 서비스 루틴 (ISR, Interrupt Service Routine)은 인터럽트 벡터에 의해 호출되는 커널 모드 내의 함수로, 현재 프로세스의 실행을 일시 중단하고 특정 하드웨어 이벤트나 소프트웨어 예외를 최우선적으로 처리하는 최하위 계층의 이벤트 핸들러이다.
- 가치: 인터럽트 원인을 식별하고 장치 상태를 갱신하며, 결과 데이터를 커널 버퍼로 전송하는 등 하드웨어와 소프트웨어 사이의 브리지 역할을 수행하여 시스템의 실시간 반응성을 결정짓는 핵심 소프트웨어 모듈이다.
- 융합: ISR은 중첩 인터럽트 (Nested Interrupt)와 우선순위 (Priority) 정책에 따라 실행 순서가 동적으로 결정되며, 실행 중 대기 (Wait/Sleep)가 불가능한 원자적 (Atomic) 특성 때문에 시스템 스케줄러 및 동기화 메커니즘과 밀접하게 연동된다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: 인터럽트 서비스 루틴 (ISR, Interrupt Service Routine)은 인터럽트 신호가 감지되었을 때 CPU가 제어권을 넘겨받아 실행하는 구체적인 코드 집합이다. 인터럽트 핸들러 (Interrupt Handler)라고도 불리며, 특정 인터럽트 번호와 1:1로 매핑되어 해당 사건에 최적화된 복구 및 처리 로직을 수행한다.
-
필요성: 하드웨어가 "신호"를 보내는 것은 일종의 호출 벨을 누르는 것과 같다. 벨이 울렸을 때 누군가 나가서 손님을 맞이하고 용건을 해결해야 하듯이, ISR은 하드웨어가 보낸 신호의 의미를 해석하고 실제 작업을 마무리하는 실질적인 '일꾼'이다. ISR이 없다면 시스템은 신호를 받아도 무엇을 해야 할지 알 수 없어 무한 루프에 빠지거나 멈추게 된다.
-
💡 비유: ISR은 고속도로 휴게소의 "정비소"와 같다. 주행 중 차에 이상 신호가 오면(인터럽트), 즉시 휴게소로 들어가 정비소 직원(ISR)이 신속하게 수리(처리)를 마치고 다시 고속도로로 복귀(IRET)하게 해주는 전용 서비스 공간이다.
-
설계 상의 핵심 문제와 진화: 초기 ISR은 모든 작업을 한 번에 끝내려 했다. 하지만 데이터 패킷 분석처럼 무거운 작업이 ISR에 포함되자, 그동안 다른 인터럽트가 차단되어 시스템이 먹통이 되는 현상이 발생했다. 이를 해결하기 위해 현대 OS는 ISR을 시급한 '상위 절반 (Top Half)'과 지연 가능한 '하위 절반 (Bottom Half)'으로 분리하는 구조로 발전했다.
ISR이 현재 프로세스의 흐름을 끊고 개입하는 시간적 선후 관계를 시각화하면 다음과 같다.
┌─────────────────────────────────────────────────────────────────────┐
│ ISR의 실행 및 제어권 전환 타이밍 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 사용자 프로세스 A: [명령어 1] ─▶ [명령어 2] [명령어 3] ─▶ │
│ │ ▲ │
│ 제어권 전환: │ (Context Save) │ (Restore) │
│ ▼ │ │
│ 인터럽트 서비스 루틴 (ISR): ┌───────────────┐ │
│ │ 1. 장치 ACK │ │
│ │ 2. 데이터 읽기 │ │
│ │ 3. 플래그 셋 │ │
│ └───────────────┘ │
│ │
│ * 핵심: 명령어 2와 3 사이의 시간 간격은 ISR 실행 시간에 비례함 │
└─────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이 그림의 핵심은 '투명성'이다. 사용자 프로세스 A는 자신이 중간에 멈췄다는 사실을 모른 채, 명령어 2번 다음에 3번이 바로 실행되었다고 믿는다. 하지만 실제로는 그 사이에 ISR이 끼어들어 CPU 자원을 점유했다. 따라서 ISR의 실행 시간이 길어질수록 프로세스 A의 입장에서는 "이유 없이 시스템이 느려진 것"처럼 느껴진다. 기술사적 관점에서 ISR은 '가장 짧고 굵게' 실행되어야 하며, 불필요한 연산이나 I/O 대기(Sleep)를 ISR 내부에 넣는 것은 시스템 전체의 응답성을 파괴하는 안티패턴이다.
- 📢 섹션 요약 비유: ISR은 달리는 마라톤 선수에게 물을 건네주는 급수대 요원과 같아서, 선수의 발걸음을 멈추게 하지 않으면서 최대한 빠르게 물만 전달하고 빠져야 합니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
- ISR 설계의 5대 골든 룰 (Constraints):
| 규칙 | 내용 | 이유 | 비유 | |:---|:---|:---|:---|:---| | Short & Fast | 가능한 최소한의 코드만 실행 | 다른 인터럽트 차단 시간 최소화 | 10초 이내 용건만 간단히 | | Non-blocking | Sleep이나 대기 함수 호출 금지 | 시스템 전체가 Deadlock에 빠질 위험 | 전화를 끊지 않고 자리 비우기 금지 | | Atomic Operation | 중단되지 않는 원자적 실행 보장 | 데이터 일관성 및 재진입성 확보 | 한 번 시작한 수술은 멈추지 않음 | | No Dynamic Alloc | 메모리 동적 할당 지양 | 할당 중 지연(Latency) 발생 가능성 배제 | 현장에서 재료 구하러 가지 않기 | | Register Save | 사용 전 모든 레지스터 보존 | 기존 프로세스 문맥 파괴 방지 | 남의 책상 쓰기 전 사진 찍어두기 |
- 중첩 인터럽트 (Nested Interrupt)와 우선순위 처리 구조: 높은 우선순위의 인터럽트가 낮은 우선순위의 ISR 실행 중에 다시 개입하는 메커니즘을 상세히 시각화한다.
┌────────────────────────────────────────────────────────────────────┐
│ 중첩 인터럽트 처리 (Nested ISR Execution) │
├────────────────────────────────────────────────────────────────────┤
│ │
│ [CPU 모드] [사용자] ──────▶ [커널 (ISR)] ──────▶ [커널 (ISR)] │
│ │
│ 우선순위: - Low (Level 5) High (Level 1) │
│ │ │ │
│ 실행 순서: (1)─────────────┐ │ │ │
│ │ │ │ │
│ (2) 인터럽트 발생(L5) │ │
│ ▼ │ │ │
│ (3) ISR_Low 실행 │ │
│ │ │ │
│ (4) 인터럽트 발생(H1) ──▶ (5) ISR_High │
│ │ 실행 │
│ (7) ISR_Low 재개 ◀─── (6) 리턴 │
│ ▼ │ │
│ (9) 사용자 앱 복구 ◀─── (8) 리턴 │
└────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 모든 인터럽트가 평등한 것은 아니다. 타이머나 하드웨어 장애 인터럽트는 마우스 입력보다 우선순위가 높아야 한다. 위 도식에서 ISR_Low가 실행되는 도중 더 급한 ISR_High가 들어오면, CPU는 현재의 ISR_Low 문맥을 다시 스택에 쌓고 ISR_High로 즉시 전환한다. 이를 '중첩 인터럽트'라 한다. 만약 모든 인터럽트의 우선순위가 같다면, 중요한 타이머 인터럽트가 사소한 입출력 처리에 밀려 시스템 시계가 느려지는 등의 심각한 문제가 발생할 수 있다. 현대의 APIC (Advanced PIC)은 이러한 우선순위 중재를 하드웨어적으로 수행하며, 운영체제 설계자는 각 장치의 '긴급도'를 고려하여 벡터 번호를 배정해야 한다.
- 심층 동작 로직 (ISR의 내부 코드 구조): 전형적인 ISR은 하드웨어로부터의 확인 신호 (ACK)와 데이터 이동, 그리고 다음 작업을 위한 신호 (Flag) 설정으로 구성된다.
/* 의사 코드로 본 전형적인 ISR 구조 */
void interrupt_service_routine(int irq_num) {
// 1. Context Save (하드웨어/컴파일러가 자동 수행하는 경우가 많음)
// 2. 하드웨어 상태 확인 및 응답 (Handshake)
if (read_register(DEV_STATUS_REG) & DATA_READY) {
write_register(DEV_CONTROL_REG, ACK_SIGNAL); // "나 받았어"라고 알림
}
// 3. 최소한의 데이터 이동
unsigned char data = read_register(DEV_DATA_REG);
push_to_kernel_buffer(data);
// 4. 하위 절반(Bottom Half) 요청
schedule_deferred_work(PROCESS_DATA_TASK); // 무거운 분석은 나중에!
// 5. Context Restore 및 리턴 (IRET)
}
[다이어그램 해설] ISR 코드의 핵심은 4번 '지연 작업 요청'이다. 예를 들어 네트워크 카드 ISR에서 패킷의 내용을 분석하고 방화벽 규칙을 적용하는 로직을 직접 수행한다면, 그 수 밀리초 동안 CPU는 다른 패킷을 받지 못해 유실이 발생한다. 따라서 유능한 ISR은 "데이터가 왔으니 커널 버퍼에 넣어뒀어. 나중에 한가할 때 분석해줘"라고 메시지만 남기고 잽싸게 퇴근(Return)한다. 이것이 인터럽트 처리의 효율성을 극대화하는 '분리 처리'의 핵심이다.
- 📢 섹션 요약 비유: 중첩 인터럽트는 응급실 의사가 경증 환자를 치료하다가(Low ISR), 심정지 환자가 실려 오면(High ISR) 즉시 그쪽으로 달려가는 것과 같은 합리적인 우선순위 판단 체계입니다.
Ⅲ. 융합 비교 및 다각도 분석 (Comparison & Synergy)
- ISR vs 일반 함수 (Function) vs 태스크 (Task) 비교:
| 비교 항목 | ISR (Interrupt Service Routine) | 일반 함수 (Function) | 태스크 (Thread/Task) |
|---|---|---|---|
| 호출 시점 | 비동기적 (하드웨어 신호) | 동기적 (코드 상의 Call) | 스케줄러에 의한 제어 |
| 실행 권한 | 커널 모드 (최상위 권한) | 호출자 모드와 동일 | 사용자 또는 커널 모드 |
| Context | 중단된 프로세스의 문맥 공유 | 호출한 프로세스의 문맥 | 자신만의 스택과 문맥 |
| 중단 가능성 | 더 높은 우선순위 인터럽트만 가능 | 언제든 선점(Preemption) 가능 | 스케줄링 정책에 따름 |
| Wait 가능성 | 절대 불가능 (Non-blocking) | 가능 | 가능 |
ISR은 일반적인 프로세스 스케줄링의 법도 밖에 있는 '특권 계층'이다. 따라서 ISR과 일반 태스크가 공유 자원을 놓고 다툴 때는 일반적인 뮤텍스 (Mutex)나 세마포어 (Semaphore)를 쓸 수 없다. ISR은 잠들 수 없기 때문이다.
┌───────────────────────────────────────────────────────────────────┐
│ ISR과 Task 간의 자원 공유 및 동기화 │
├───────────────────────────────────────────────────────────────────┤
│ │
│ [일반 Task] ──────▶ [공유 자원 (Shared)] ◀────── [ISR] │
│ │
│ 동기화 전략: │
│ - Task 쪽: 인터럽트 금지 (Disable Interrupt) 후 접근 │
│ - ISR 쪽: 그냥 접근 (어차피 Task는 개입 못함) │
│ │
│ ❌ 금지: ISR에서 Spinlock 대기나 Mutex Lock 시도 │
│ ✅ 권장: Lock-free Queue 사용, Atomic Variable 활용 │
│ │
│ * 이유: ISR이 Lock을 기다리며 대기하면 시스템이 굳어버림 │
└───────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 이것은 운영체제 개발에서 가장 흔히 발생하는 버그 중 하나다. 일반 태스크가 자원을 점유한 상태에서 인터럽트가 발생하여 ISR이 실행되었는데, ISR이 그 자원을 쓰려고 대기(Wait)를 시작하면 어떻게 될까? 태스크는 ISR이 끝나야 다시 실행되어 자원을 풀어줄 수 있고, ISR은 태스크가 자원을 풀어줘야 끝날 수 있는 '데드락 (Deadlock)'에 즉시 빠진다. 따라서 ISR과 데이터를 주고받을 때는 인터럽트를 일시적으로 끄는 local_irq_save() 같은 원자적 보호막을 치거나, 대기가 필요 없는 Lock-free 자료구조를 사용하는 것이 기술사적 정석이다.
- 📢 섹션 요약 비유: ISR은 멈출 수 없는 "컨베이어 벨트"와 같아서, 물건(자원)이 없다고 벨트를 멈춰버리면 공장 전체가 가동 중단되는 사고가 발생하는 것과 같습니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
-
실무 시나리오 및 최적화 전략:
- ISR 재진입성 (Reentrancy) 문제: 동일한 인터럽트가 ISR 실행 중에 다시 발생할 경우, 전역 변수가 꼬일 수 있다. 현대 OS는 동일 라인의 인터럽트를 ISR 실행 동안 하드웨어적으로 마스킹하여 재진입을 막거나, 재진입 가능한 Reentrant 코드로 작성하여 안정성을 높인다.
- 공유 스택 오버플로우: ISR은 보통 중단된 프로세스의 커널 스택을 빌려 쓴다. 만약 중첩 인터럽트가 너무 깊어지거나 ISR 내부에 큰 지역 변수를 선언하면 스택이 터져 시스템이 크래시(Crash)된다. ISR 내부에서는 재귀 호출을 금지하고 스택 사용을 극도로 아껴야 한다.
- 지연된 프로시저 호출 (DPC/SoftIRQ): 실무에서 "ISR이 너무 느리다"는 판정이 나오면 로직을 분할해야 한다. 하드웨어 레지스터만 건드리고 종료한 뒤, 나머지는 시스템의 여유 시간에 처리하도록
WorkQueue등에 넘기는 아키텍처적 결단이 필요하다.
임베디드 및 서버 환경에서 ISR의 성능 병목을 진단하고 개선하는 실무 체크리스트는 다음과 같다.
┌─────────────────────────────────────────────────────────────────┐
│ ISR 성능 진단 및 최적화 체크리스트 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ [ ] 실행 시간이 100us를 초과하는가? (스코프로 측정) │
│ [ ] 루프(while/for) 문이 포함되어 있는가? │
│ [ ] printf() 같은 블로킹 함수를 사용 중인가? │
│ [ ] ISR 내부에서 동적 메모리(malloc)를 할당하는가? │
│ [ ] 모든 레지스터를 올바르게 복구하고 있는가? │
│ │
│ ☞ 하나라도 'Yes'라면 아키텍처 재설계(Bottom Half 분리) 필요 │
└─────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 특히 신입 엔지니어들이 범하는 가장 큰 실수가 ISR 내부에서 디버깅을 위해 printf를 남발하는 것이다. printf는 직렬 포트나 콘솔 출력을 기다리는 '블로킹' 동작이 포함되어 있어, ISR의 실행 시간을 기하급수적으로 늘린다. 실무에서는 ISR 전용 로그 버퍼를 만들어 메모리에만 기록하고, 나중에 일반 프로세스가 이를 읽어가게 하는 '비동기 로깅'이 필수다. 기술사적 판단으로 ISR의 품질은 "코드의 화려함이 아니라 실행의 간결함"에서 결정된다.
- 📢 섹션 요약 비유: ISR 최적화는 1초가 급한 F1 경기장의 "피트 스탑(Pit Stop)"과 같아서, 타이어만 갈고 바로 내보내야지 엔진 전체를 점검(과도한 로직)하려고 들면 경기에 지게 되는 것과 같습니다.
Ⅴ. 기대효과 및 결론 (Future & Standard)
-
기대효과: 잘 설계된 ISR은 시스템에 '민첩성'을 부여한다. 사용자가 느끼는 끊김 없는 인터페이스, 데이터 유실 없는 초고속 통신, 정확한 타이밍의 제어 시스템은 모두 백그라운드에서 수백만 번씩 묵묵히 실행되는 ISR의 헌신 덕분이다. 또한 사건 기반 프로그래밍 (Event-driven Programming) 패러다임의 가장 낮은 토대가 되어 자원 효율성을 극대화한다.
-
미래 전망: 멀티코어 분산 처리가 정교해짐에 따라, 인터럽트 부하를 동적으로 모니터링하여 ISR 실행 코어를 실시간으로 옮기는 'Dynamic ISR Migration' 기술이 중요해질 것이다. 또한 보안 강화를 위해 ISR 실행 영역을 하드웨어적으로 격리하여, ISR을 통한 커널 권한 탈취 공격을 차단하는 가상화 기반 보안 (VBS) 기술이 표준이 될 전망이다.
-
📢 섹션 요약 비유: ISR은 보이지 않는 곳에서 24시간 깨어 있는 "야간 경비원"과 같아서, 평소에는 존재를 느끼지 못하지만 비상 상황(인터럽트)이 발생했을 때 가장 먼저 달려와 질서를 지켜주는 든든한 보호자입니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| Top Half / Bottom Half | ISR의 실행 시간을 줄이기 위해 작업을 시급한 것과 미룰 것으로 나누는 전략. |
| 재진입성 (Reentrancy) | ISR이 자기 자신에 의해 다시 호출되어도 안전하게 동작할 수 있는 코드 설계 특성. |
| 문맥 저장 (Context Save) | ISR 실행 전 현재 CPU 상태를 보호하기 위해 레지스터를 스택에 대피시키는 필수 절차. |
| SoftIRQ / Tasklet | ISR(상위 절반)로부터 넘겨받은 작업을 처리하는 운영체제의 지연 처리 메커니즘. |
| 우선순위 역전 (Inversion) | 낮은 우선순위 ISR이 높은 우선순위 태스크를 방해하지 않도록 관리해야 하는 설계 이슈. |
👶 어린이를 위한 3줄 비유 설명
- ISR은 컴퓨터 안의 "긴급 구조대"예요. 벨이 울리면(인터럽트) 하던 일을 다 멈추고 쌩쌩 달려가서 도와주죠.
- 구조대는 아주 빠르게 일을 처리하고 돌아와야 해요. 너무 오래 자리를 비우면 다른 친구들이 위험할 때 도와줄 수 없으니까요.
- 이 구조대 아저씨들 덕분에 컴퓨터는 우리가 마우스를 움직이거나 노래를 들을 때 하나도 놓치지 않고 척척 처리할 수 있답니다!