인터럽트가 무엇인지 설명해 주세요.
CPU가 현재 실행 중인 작업을 일시 중단하고 긴급한 이벤트를 먼저 처리한 뒤 원래 작업으로 복귀하는 메커니즘입니다.
인터럽트 (넓은 의미)
│
├── 하드웨어 인터럽트 (외부 인터럽트)
│ │
│ ├── 마스커블 (Maskable) ── cli로 비활성화 가능
│ │ ex) 키보드, 디스크, NIC, 타이머
│ │
│ └── 논마스커블 (NMI) ── 비활성화 불가
│ ex) 전원 이상, 메모리 패리티 오류
│
└── 소프트웨어 인터럽트 (내부 인터럽트)
│
├── 트랩 (Trap) ── 의도적 발생
│ ex) 시스템 콜, 디버그 브레이크포인트
│
└── 예외 (Exception) ── 비의도적 발생
├── Fault ── 복구 후 같은 명령어 재실행 (페이지 폴트)
├── Trap ── 다음 명령어로 진행 (오버플로우)
└── Abort ── 복구 불가 (Double Fault)
| 항목 | 하드웨어 인터럽트 | 소프트웨어 인터럽트 |
| 발생 주체 | CPU 외부 하드웨어 장치 | 프로그램 내부 명령어 |
| 동기/비동기 | 비동기적 (언제든 발생) | 동기적 (명령어 실행 시점) |
| 의도성 | 비의도적 (외부 이벤트) | 의도적(트랩) 또는 비의도적(예외) |
| 전달 경로 | 장치 → PIC/APIC → CPU | 명령어 → CPU 직접 처리 |
| 예시 | 키보드 입력, 타이머, 디스크 완료 | 시스템 콜, 0으로 나누기, 페이지 폴트 |
인터럽트 처리 흐름
① 인터럽트 발생
├── 하드웨어: 장치 → 인터럽트 컨트롤러 → CPU INTR 핀
└── 소프트웨어: 프로그램 내 syscall/int 명령어 실행
② CPU가 현재 명령어 실행 완료
③ 현재 상태 저장 (PC, 레지스터, 플래그 → 스택)
④ IDT(Interrupt Descriptor Table)에서 벡터 번호로 핸들러 조회
⑤ 커널 모드 전환 (모드 비트 1 → 0)
⑥ ISR(Interrupt Service Routine) 실행
⑦ 저장된 상태 복원
⑧ 원래 프로그램으로 복귀 (iret 명령)
프로그램 실행 중
────────●──────────────────────●──────────
│ 인터럽트 발생 │ 복귀
▼ ▲
┌───────────────────────────────┐
│ ① 상태 저장 │
│ ② IDT → 핸들러 주소 조회 │
│ ③ ISR 실행 │
│ ④ 상태 복원 │
└───────────────────────────────┘
커널 모드에서 처리
IDT (Interrupt Descriptor Table)의 벡터 할당
| 벡터 범위 | 용도 | 예시 |
| 0 ~ 31 | CPU 예외 (Intel 예약) | 0: Divide Error, 13: GPF, 14: Page Fault |
| 32 ~ 47 | 하드웨어 인터럽트 (IRQ) | 32: 타이머, 33: 키보드, 46: 디스크 |
| 48 ~ 255 | OS/사용자 정의 | 128(0x80): Linux 시스템 콜 |
하드웨어 인터럽트 처리 구조
하드웨어 장치 인터럽트 컨트롤러 CPU
┌─────────┐ ┌──────────────┐ ┌──────────────┐
│ 키보드 │─IRQ1─→│ │ │ │
│ 디스크 │─IRQ14→│ PIC / APIC │─INTR→│ IDT 조회 │
│ NIC │─IRQ11→│ (우선순위 판단)│ │ ISR 실행 │
│ 타이머 │─IRQ0─→│ │ │ EOI 전송 │
└─────────┘ └──────────────┘ └──────────────┘
인터럽트 우선순위
| 우선순위 | 인터럽트 종류 |
| 최상위 | 전원 이상 (NMI) |
| ↑ | 하드웨어 오류 (Machine Check) |
| ↑ | 타이머 인터럽트 |
| ↑ | 디스크 I/O 인터럽트 |
| ↑ | 네트워크 인터럽트 |
| ↑ | 키보드 인터럽트 |
| 최하위 | 소프트웨어 인터럽트 (트랩) |
높은 우선순위의 인터럽트는 낮은 우선순위의 ISR 실행 중에도 선점(Preemption)할 수 있습니다.
ISR(키보드) 실행 중
│
├── 타이머 인터럽트 발생 (높은 우선순위) → 키보드 ISR 선점, 타이머 먼저 처리
│
└── 마우스 인터럽트 발생 (낮은 우선순위) → 대기, 키보드 ISR 완료 후 처리
폴링 vs 인터럽트
| 항목 | 폴링 (Polling) | 인터럽트 (Interrupt) |
| 방식 | CPU가 주기적으로 장치 상태 확인 | 장치가 CPU에 이벤트 알림 |
| CPU 사용 | 이벤트 없어도 CPU 낭비 | 이벤트 발생 시에만 CPU 사용 |
| 응답 속도 | 폴링 주기에 의존 | 즉시 감지 가능 |
| 구현 복잡도 | 단순 | 상대적으로 복잡 |
| 적합한 상황 | 이벤트가 매우 빈번한 경우 | 이벤트 발생이 불규칙한 경우 |
[폴링]
CPU: 데이터 있어? → 없음
CPU: 데이터 있어? → 없음
CPU: 데이터 있어? → 없음 ← CPU 시간 낭비
CPU: 데이터 있어? → 있음! → 처리
[인터럽트]
CPU: (다른 작업 수행 중...)
장치: 데이터 왔어! (인터럽트) ← CPU 낭비 없음
CPU: (중단) → 처리 → (복귀)
Top Half vs Bottom Half
인터럽트 처리를 두 단계로 나누어 시스템 응답성을 유지합니다.
| 항목 | Top Half (상반부) | Bottom Half (하반부) |
| 실행 시점 | 인터럽트 발생 즉시 | Top Half 완료 후 나중에 |
| 인터럽트 상태 | 비활성화 (다른 인터럽트 차단) | 활성화 (다른 인터럽트 허용) |
| 처리 내용 | 최소한의 긴급 작업 | 시간 소요가 큰 후속 작업 |
| 예시 | 장치에서 데이터 읽기, 플래그 설정 | 데이터 가공, 프로세스 깨우기 |
네트워크 패킷 수신 예시:
[Top Half — 즉시 실행]
① NIC에서 패킷 데이터 읽기
② 커널 버퍼에 저장
③ Bottom Half(Softirq) 등록
④ EOI 전송
[Bottom Half — 나중에 실행]
⑤ 패킷 파싱 (IP, TCP 헤더 분석)
⑥ TCP 재조립
⑦ 소켓 버퍼에 데이터 전달
⑧ read()로 대기 중인 프로세스 깨우기
Java에서 인터럽트가 발생하는 상황
// 1. 콘솔 출력 → 시스템 콜(트랩) 발생
System.out.println("hello"); // write() → 소프트웨어 인터럽트
// 2. 키보드 입력 → 하드웨어 인터럽트 발생
Scanner sc = new Scanner(System.in);
String input = sc.nextLine(); // 키 입력마다 IRQ1 하드웨어 인터럽트
// 3. 파일 읽기 → 시스템 콜(트랩) + 디스크 인터럽트
FileInputStream fis = new FileInputStream("data.txt"); // open() 트랩
byte[] buf = new byte[1024];
fis.read(buf); // read() 트랩 → 디스크 I/O → 완료 시 하드웨어 인터럽트
// 4. 네트워크 → 시스템 콜(트랩) + NIC 인터럽트
Socket socket = new Socket("example.com", 80); // socket()+connect() 트랩
socket.getInputStream().read(); // 패킷 도착 시 NIC 인터럽트
// 5. 연산 오류 → 예외 (소프트웨어 인터럽트)
int result = 10 / 0; // CPU Divide Error → ArithmeticException
// 6. Thread.sleep → 시스템 콜(트랩) + 타이머 인터럽트
Thread.sleep(1000); // nanosleep() 트랩 → 1초 후 타이머 인터럽트로 깨움
Java 프로그램 실행 중 발생하는 인터럽트 타임라인:
사용자 모드 ●━━━●━━━━━●━━━━━━━●━━━●━━━━━━━━━━━●
│ │ │ │ │
트랩 하드웨어 트랩 예외 트랩
(write) (키보드) (read) (÷0) (nanosleep)
│ │ │ │ │
커널 모드 ●━● ●━● ●━● ●━● ●━●