입출력 관리 (인터럽트, DMA, 버퍼)
운영체제의 메모리·CPU·파일 관리가 컴퓨터 안쪽의 자원을 다뤘다면, 입출력 관리는 컴퓨터 바깥 세계와 통신하는 책임을 진다. 키보드·마우스·디스크·네트워크 카드·프린터처럼 속도와 동작 방식이 천차만별인 외부 장치들을 일관된 인터페이스로 추상화하고, CPU의 시간을 낭비하지 않으면서 효율적으로 데이터를 주고받게 만드는 일이 입출력 관리의 핵심이다. 본 글은 입출력 방식 3종(폴링·인터럽트·DMA)과 인터럽트 처리, 그리고 버퍼링·스풀링·캐싱 같은 성능 최적화 기법을 세심하게 정리한다(출처: 위키백과 — Input/Output). 제가 학교 OS 실습에서 같은 1MB 파일을 폴링과 DMA로 읽어 비교했을 때 CPU 사용률이 95%에서 5%로 떨어지는 모습을 직접 본 후로는 "DMA 없는 시스템은 사실상 사용 불가능"이라는 인상을 손끝으로 받아들였다.

입출력 방식 3종 — 폴링·인터럽트·DMA
입출력 장치는 CPU보다 수백·수천 배 느리기 때문에, 같은 작업을 어떻게 처리하느냐에 따라 CPU의 사용률이 크게 달라진다. 시험과 실무 모두에서 자주 출제되는 세 가지 핵심 방식은 다음과 같다.
| 방식 | CPU 관여 | 효율 | 대표 사용처 |
|---|---|---|---|
| 폴링(Polling) | 매우 높음 (반복 확인) | 매우 낮음 | 단순 임베디드 |
| 인터럽트(Interrupt) | 보통 (필요할 때만) | 보통 | 키보드·마우스·네트워크 |
| DMA | 매우 낮음 (블록 단위 위임) | 매우 높음 | 디스크·네트워크 대용량 |
폴링(Polling)은 가장 단순한 방식으로, CPU가 입출력 장치의 상태 레지스터를 주기적으로 확인하면서 데이터 준비 여부를 검사한다. 구현이 단순하지만 CPU가 다른 작업을 할 수 없고, 장치가 준비될 때까지 의미 없는 검사를 반복한다는 결정적 약점이 있다. 그래서 폴링은 단순한 마이크로컨트롤러나 시간 임계가 매우 짧은 상황에 한해서만 사용된다.
인터럽트(Interrupt)는 CPU가 다른 작업을 하다가 입출력 장치가 데이터를 준비했을 때 그 장치가 CPU에 신호(인터럽트)를 보내 알리는 방식이다. 여기서 인터럽트란 CPU가 정해진 흐름의 명령 실행을 잠시 멈추고 외부 신호에 우선 대응하도록 만드는 비동기 이벤트 메커니즘을 가리킨다. CPU는 신호를 받기 전까지 자기 일을 계속 처리할 수 있어 폴링 대비 압도적인 효율을 보인다. 다만 인터럽트 자체에도 처리 비용이 들기 때문에, 초당 수만 개 이상의 인터럽트가 발생하면 CPU 시간이 인터럽트 처리에 묶이는 인터럽트 폭풍(interrupt storm) 사고가 발생할 수 있다.
DMA(Direct Memory Access)는 CPU를 거치지 않고 입출력 장치가 메모리에 직접 데이터를 옮기게 하는 하드웨어 기법이다. CPU는 단지 "어디서 어디로 얼마나 옮겨라"는 명령만 DMA 컨트롤러에 전달하고 자기 일을 계속한다. 전송이 끝나면 DMA 컨트롤러가 인터럽트로 CPU에 완료를 알린다. 대용량 데이터(디스크·네트워크·GPU)를 다룰 때 DMA 없이는 사실상 운영이 불가능하며, 현대 거의 모든 시스템이 DMA를 표준으로 채택한다.
폴링 인터럽트 DMA
──────── ──────── ────────
CPU: while(!ready){} CPU: 다른 작업 중 CPU: 명령만 전달
CPU: 데이터 복사 장치 → 인터럽트 신호 장치 ↔ 메모리 직접
CPU: 다음 작업 CPU: 핸들러 → 복사 CPU: 완료 인터럽트만 받음
CPU 100% 묶임 CPU 일부 사용 CPU 거의 자유
인터럽트 처리 메커니즘과 분류
인터럽트가 발생하면 CPU는 정해진 순서로 처리한다. 시험에서 자주 출제되는 핵심 단계는 다음 다섯 가지다(출처: 위키백과 — Interrupt).
- 인터럽트 요청 감지 — PIC(Programmable Interrupt Controller) 또는 APIC가 신호 수집
- 현재 상태 저장 — 프로그램 카운터(PC)·플래그·레지스터를 스택에 푸시
- 인터럽트 벡터 조회 — 인터럽트 벡터 테이블에서 해당 ISR(Interrupt Service Routine) 주소 검색
- ISR 실행 — 핸들러 코드가 인터럽트 처리
- 상태 복원 — 저장한 PC·레지스터를 복원해 원래 작업 재개
여기서 ISR(Interrupt Service Routine)이란 특정 인터럽트가 발생했을 때 자동으로 호출되는 짧은 처리 함수를 가리키며, 인터럽트 벡터 테이블에 미리 등록되어 있다. ISR은 가능한 한 짧게 작성하는 것이 원칙이며, 긴 작업은 나중에 처리할 작업 큐에 넣어두고 ISR은 즉시 종료한다.
인터럽트는 발생 원인에 따라 두 종류로 나뉜다. 시험 답안에서 자주 묻는 항목이다.
| 종류 | 발생 원인 | 예시 |
|---|---|---|
| 하드웨어 인터럽트 | 외부 장치 신호 | 키보드 입력, 디스크 완료, 타이머 만료 |
| 소프트웨어 인터럽트 | 명령어로 발생 | 시스템 콜, 0으로 나누기, 페이지 부재 |
소프트웨어 인터럽트는 트랩(Trap) 또는 예외(Exception)라고도 부르며, 응용 프로그램이 운영체제 기능을 호출하는 표준 경로인 시스템 콜이 정확히 이 방식을 통해 동작한다. 솔직히 제 경험상 학교에서 처음 strace로 한 줄짜리 printf가 만들어내는 시스템 콜 흐름을 봤을 때 write 호출 한 줄에 인터럽트 → 커널 진입 → 디스크 I/O → 인터럽트 복귀라는 거대한 흐름이 숨어 있다는 사실에 큰 인상을 받았다.
인터럽트에는 또한 우선순위(Priority)가 있다. 동시에 여러 인터럽트가 발생하면 우선순위가 높은 것부터 처리되며, 처리 중인 인터럽트보다 더 우선순위 높은 인터럽트가 들어오면 현재 처리를 중단하고 새 인터럽트를 먼저 처리하는 중첩(nested) 인터럽트 흐름이 표준이다.
버퍼링·스풀링·캐싱 — 입출력 성능 최적화
CPU와 입출력 장치의 속도 차이를 메우는 세 가지 표준 기법이 있다. 시험과 실무 모두에서 자주 출제되는 항목이다.
버퍼링(Buffering)은 데이터가 흘러가는 중간에 메모리 영역(버퍼)을 두고 일정량을 모은 뒤 한꺼번에 전송하는 기법이다. 한 바이트씩 천천히 들어오는 키보드 입력을 버퍼에 모았다가 줄 단위로 처리하거나, 한 글자씩 디스크에 쓰지 않고 한 블록씩 모아 쓰는 흐름이 대표적이다. 버퍼링의 강점은 느린 장치와 빠른 장치의 속도 차이를 흡수한다는 점이며, C언어의 printf도 사실은 버퍼링되어 있어 fflush나 개행 문자를 만나야 비로소 화면에 출력된다.
#include <stdio.h>
int main(void) {
printf("Hello"); // 버퍼에 저장만 됨 (출력 안 보임)
// fflush(stdout); // 강제로 비우면 즉시 출력
sleep(2); // 2초 대기 동안에도 화면 비어 있음
printf("\n"); // 개행 만나면 비로소 버퍼 플러시
return 0;
}
스풀링(Spooling)은 SPOOL(Simultaneous Peripheral Operations On-Line)의 약자로, 여러 사용자가 동시에 사용하려는 장치(주로 프린터)의 입출력을 디스크에 임시 저장해 두고 순차적으로 처리하는 기법이다. 여기서 스풀링이란 느린 입출력 장치의 작업을 디스크 큐에 쌓아 두고 응용 프로그램은 즉시 다음 작업으로 넘어가게 만드는 비동기 처리 패턴을 의미한다. 프린터 작업이 길게 큐에 쌓이는 모습이 가장 대표적인 스풀링이며, 사용자는 인쇄 명령을 내리고 즉시 다른 일을 할 수 있다.
| 비교 항목 | 버퍼링(Buffering) | 스풀링(Spooling) |
|---|---|---|
| 저장 위치 | 주 메모리(RAM) | 디스크 |
| 동시성 | 한 작업 안의 흐름 동기화 | 여러 작업의 큐잉 |
| 대표 사용처 | 키보드·파일 I/O | 프린터·배치 작업 |
| 본질 | 속도 차이 흡수 | 작업 분리·비동기화 |
캐싱(Caching)은 자주 쓰이는 데이터를 더 빠른 저장 매체에 미리 두는 기법이다. 디스크 데이터를 RAM에 캐시해 두는 페이지 캐시, 자주 쓰는 inode를 메모리에 보관하는 inode 캐시가 모두 같은 부류이다. 캐싱의 핵심 원리는 지역성(locality)으로, 한 번 참조된 데이터는 가까운 시간 안에 다시 참조될 가능성이 높다는 시간 지역성과, 한 데이터 근처의 데이터가 함께 참조될 가능성이 높다는 공간 지역성이 결합되어 캐시 적중률을 높인다. 솔직히 이건 예상 밖이었는데, 학교 실습에서 같은 파일을 두 번째로 읽었을 때 첫 번째보다 100배 빠른 모습을 본 후로는 페이지 캐시가 운영체제의 가장 큰 성능 비밀이라는 점을 손끝으로 받아들였다.
마지막으로 시험 답안에서 자주 쓰이는 정형 표현을 정리하면, "입출력 관리는 폴링·인터럽트·DMA 세 가지 방식으로 구현되며, 인터럽트는 하드웨어 인터럽트와 소프트웨어 인터럽트(트랩)로 분류된다. 속도 차이를 흡수하기 위한 표준 기법으로 버퍼링·스풀링·캐싱이 있으며, 현대 운영체제는 거의 모두 DMA + 인터럽트 + 페이지 캐시의 조합으로 입출력을 운영한다"는 두 문장이 표준 답안 표현으로 통한다.
메타 디스크립션: 운영체제 입출력 관리의 3대 방식(폴링·인터럽트·DMA), 인터럽트 처리 메커니즘과 하드웨어·소프트웨어 인터럽트 분류, 그리고 버퍼링·스풀링·캐싱 같은 성능 최적화 기법을 OS 입문자 관점에서 세심하게 정리합니다.