Chapter 13 · I/O Systems

입출력 시스템 — 하드웨어 구조부터 소프트웨어 계층까지

요약

운영체제의 I/O system은 다양한 장치를 균일하게 다루기 위해 하드웨어부터 사용자 공간까지 여러 계층으로 구성된다. PC의 bus 구조 위에 연결된 장치들은 device controller를 통해 CPU·메모리와 통신하며, 각 컨트롤러는 명령·상태·데이터를 담는 register를 제공한다.

장치에 접근하는 방식은 direct I/O(전용 I/O 명령 사용)와 memory-mapped I/O (레지스터를 주소 공간에 매핑) 두 가지로 나뉜다. CPU가 장치 완료 여부를 파악하는 방법으로는 CPU가 주기적으로 상태를 확인하는 polling과 장치가 완료 시 CPU에게 알리는 interrupt 방식이 있다. 대용량 데이터 전송에는 CPU 개입 없이 장치와 메모리 사이를 직접 연결하는 DMA가 사용된다.

I/O 소프트웨어는 하드웨어에 가장 가까운 interrupt handler, 장치별 제어 코드인 device driver, 장치 독립적 공통 서비스를 담당하는 device-independent I/O software, 그리고 표준 라이브러리를 제공하는 user-space I/O software 계층으로 구성된다. 각 계층은 아래 계층의 세부 사항을 숨겨 장치 독립성을 실현한다.

핵심개념

block device 블록 장치

고정 크기(512B–32KB)의 블록 단위로 데이터를 저장·전송하며, 각 블록에 주소가 있어 임의 접근(random access)이 가능하다. 다른 블록과 독립적으로 읽기·쓰기가 가능하다. 디스크, 테이프 등이 해당된다.

character device 문자 장치

바이트 스트림을 순서대로 전달하거나 받는 장치. 주소가 없고 seek 연산이 불가능하다. 프린터, 모뎀, 마우스, 키보드 등이 해당된다.

device controller 장치 컨트롤러

I/O 장치의 전자 구성 요소로, 직렬 비트 스트림을 바이트 블록으로 변환하고 오류 정정을 수행하며 데이터를 주 메모리에 제공한다. 하나의 컨트롤러가 여러 장치를 관리할 수 있다.

I/O port / registers I/O 포트 / 레지스터

장치 컨트롤러가 가진 레지스터 집합(data-in, data-out, status, control). device driver가 여기에 명령·주소·데이터를 기록하거나 읽어서 장치를 제어한다. 보통 1–4바이트 또는 FIFO 버퍼로 구성된다.

memory-mapped I/O 메모리 매핑 I/O

장치 컨트롤러 레지스터를 프로세서의 주소 공간에 매핑하여, 일반 load/store 명령으로 I/O를 수행하는 방식. C로 드라이버 작성이 가능하며 PTE로 보호한다.

direct I/O 직접 I/O (포트 I/O)

전용 I/O 명령어(Intel의 in, out 등)로 I/O 포트 주소에 접근하는 방식. 메모리 주소 공간과 분리된 별도의 I/O 주소 공간을 사용한다.

polling 폴링

CPU가 주기적으로 장치의 상태 레지스터를 읽어 완료 여부를 확인하는 방식(busy-wait). 구현이 단순하나, 장치가 빠르게 응답하지 않으면 CPU 낭비가 심하다.

interrupt 인터럽트

장치가 작업 완료(또는 오류) 시 CPU에 신호를 보내는 메커니즘. CPU는 평소에 다른 작업을 수행하다가 interrupt service routine(ISR)으로 분기해 처리한다. 여러 장치 간 공유도 가능하다.

interrupt-driven I/O 인터럽트 구동 I/O

I/O 요청 후 CPU는 다른 작업을 계속하다가, 장치가 완료 시 interrupt를 발생시키면 그때 ISR을 통해 처리한다. polling보다 일반적으로 효율적이나, 과도한 인터럽트는 오버헤드를 유발한다.

programmed I/O (PIO) 프로그램 I/O

데이터 전송 시 CPU가 직접 I/O 장치와 메모리 사이에서 데이터를 옮기는 방식. 전용 I/O 명령 또는 memory-mapped I/O를 통해 구현한다. CPU가 전송 내내 관여해 부하가 높다.

DMA 직접 메모리 접근

DMA controller가 CPU 개입 없이 장치 버퍼에서 주 메모리로(또는 반대로) 데이터 블록을 직접 전송하는 방식. 블록 단위로 단 하나의 인터럽트만 발생시켜 CPU 부하를 크게 줄인다.

blocking I/O 블로킹 I/O

I/O 완료까지 프로세스가 대기(suspend)하는 방식. read(), write() 등이 대표적. 사용과 이해가 단순하다.

non-blocking I/O 논블로킹 I/O

I/O 호출이 즉시 반환되며, 실제로 전송된 바이트 수를 반환 값으로 전달한다. 멀티스레딩으로 구현하며 select()로 데이터 준비 여부를 확인한다.

asynchronous I/O 비동기 I/O

I/O 요청 후 프로세스가 계속 실행되다가, 완료 시 운영체제가 시그널·콜백 등을 통해 통지하는 방식. 완료 전까지 얼마나 전송됐는지 알 수 없다.

device driver 장치 드라이버

각 I/O 장치를 제어하는 장치별 코드. device-independent I/O softwareinterrupt handler와 상호작용한다. 커널에 정적 링크되거나 부팅 시 또는 실행 중 동적으로 로드될 수 있다.

device-independent I/O layer 장치 독립적 I/O 계층

모든 장치에 균일한 인터페이스를 제공하는 계층. Unix에서 장치를 특수 파일로 모델링하며, open(), read(), write(), ioctl() 등의 시스템 콜로 접근한다. 오류 보고와 버퍼링을 담당한다.

개념정리

1. I/O 하드웨어 구조 (Bus / Controller)

일반적인 PC는 bus 구조를 통해 CPU, 메모리, 다양한 I/O 장치를 연결한다. Kernel I/O Structure 다이어그램에서 볼 수 있듯 커널은 버스 위의 각 장치와 device controller를 통해 통신한다.

device controller(또는 host adapter)는 I/O 장치의 전자 구성 요소로 다음 역할을 수행한다.

  • 직렬 비트 스트림을 바이트 블록으로 변환
  • 데이터를 주 메모리에서 사용 가능하도록 만들기
  • 필요 시 오류 정정 수행
  • 하나의 컨트롤러가 여러 장치를 처리 가능

각 컨트롤러에는 register가 있어 device driver가 명령·주소·데이터를 기록하거나 읽는다. 레지스터 종류: data-in register, data-out register, status register, control register. 보통 1–4바이트 또는 FIFO 버퍼 크기이다.

참고 — 단순 프린터 예시 status registerdone bit가 문자 출력 완료를 나타내고, error bit가 용지 걸림·용지 부족 등 오류를 나타낸다. data register에 인쇄할 데이터를 쓴다. CPU는 done bit가 설정될 때까지 대기하고 error bit를 확인해야 한다.

2. I/O Devices (Block vs. Character)

Block Device vs. Character Device 비교
구분 block device character device
전송 단위 고정 크기 블록 (512B–32KB) 바이트 스트림
주소 지정 각 블록에 주소 있음, 임의 접근 가능 주소 없음, seek 불가
독립 접근 다른 블록과 무관하게 읽기/쓰기 가능 순차 스트림만 가능
예시 디스크, 테이프 프린터, 모뎀, 마우스, 키보드

슬라이드의 장치 속도 표에서 볼 수 있듯 인터페이스 종류에 따라 전송 속도 차이가 크다. USB 2.0(60 MB/s)에서 NVMe M.2(20 Gb/s), PCIe 5.0(4 GB/s)까지 넓은 범위에 걸친다.

3. Accessing Devices (Direct I/O vs. Memory-mapped I/O)

I/O 접근 방식 비교
구분 direct I/O (포트 I/O) memory-mapped I/O
주소 공간 별도의 I/O 포트 주소 공간 프로세서 메모리 주소 공간에 매핑
사용 명령 전용 I/O 명령 (in, out, ins, insb, insl, insw 등) 일반 load / store 명령
드라이버 언어 어셈블리 필요할 수 있음 C로 완전히 작성 가능
보호 방식 커널 모드 전용 명령으로 보호 PTE(page table entry)로 보호; 사용자에게 특정 장치 제어 권한 부여 가능
레지스터 테스트 여러 명령 필요할 수 있음 단일 명령으로 레지스터 읽기·테스트 가능
참고 memory-mapped I/O에서는 특별한 보호 메커니즘이 불필요하다. 페이지 테이블에 해당 페이지를 포함시켜 특정 사용자 프로세스가 특정 장치를 직접 제어하도록 권한을 부여할 수 있다.

4. Polling vs. Interrupts

CPU가 I/O 요청 처리 여부를 어떻게 알 수 있는가에 대한 두 가지 접근이 있다.

Polling vs. Interrupt-driven I/O 비교
구분 polling (Polled I/O) interrupt-driven I/O
동작 방식 CPU가 주기적으로 장치 상태 레지스터를 확인 장치가 완료 시 CPU에 인터럽트 신호 전송
장점 구현 단순, 소프트웨어가 제어권 유지, 장치가 빠르면 효율적 필요할 때만 CPU가 장치 응답, 일반적으로 더 효율적
단점 비자명 시스템에서 CPU 이용률 높음(낭비), 낮은 우선순위 장치 서비스 불가 과도한 인터럽트는 프로그램 실행 방해, 바이트당 1회 인터럽트 오버헤드

Interrupt-driven I/O 동작 흐름:

  1. CPU가 장치에 I/O 명령을 내린다.
  2. CPU는 다른 작업을 계속 수행한다.
  3. 장치가 작업을 마치면 interrupt를 발생시킨다.
  4. CPU가 현재 작업을 저장하고 해당 장치의 interrupt service routine(ISR)으로 분기한다.
  5. ISR이 완료되면 CPU는 이전 작업으로 복귀한다.
참고 인터럽트는 여러 장치 간에 공유(shared)될 수 있다.

5. Programmed I/O vs. DMA

Programmed I/O vs. DMA 비교
구분 programmed I/O (PIO) DMA (Direct Memory Access)
CPU 역할 데이터 전송 내내 CPU가 직접 관여 전송 시작만 지시; 이후 CPU 개입 없음
전송 주체 CPU가 장치 ↔ 메모리 사이 데이터 이동 DMA controller가 장치 버퍼 → 주 메모리 직접 전송
인터럽트 발생 바이트·워드 단위마다 발생 가능 블록 단위로 1회만 발생
적합한 장치 저속 장치, 소량 전송 고속 장치, 대용량 데이터 전송
CPU 효율 낮음 (전송 중 CPU 점유) 높음 (전송 중 CPU 다른 작업 가능)

DMA는 메모리 속도에 근접한 속도로 데이터를 전송할 수 있는 고속 I/O 장치에 사용된다. DMA controller는 CPU를 우회(bypass)하여 장치와 메모리 사이의 데이터를 직접 전송하며, programmed I/O의 대규모 데이터 이동 부담을 없애기 위해 사용된다.

6. Blocking vs. Non-blocking I/O

I/O 대기 방식 비교
구분 blocking I/O non-blocking I/O asynchronous I/O
호출 반환 시점 I/O 완료 후 즉시 (전송된 바이트 수 반환) 즉시 (완료는 나중에 통지)
프로세스 상태 I/O 중 대기(suspend) 계속 실행 (멀티스레딩) 계속 실행
완료 확인 호출 반환이 곧 완료 select()로 준비 확인 시그널·콜백 등 통지 방식
대표 시스템 콜 read(), write() select() + read() aio_read()
사용 난이도 단순, 이해 쉬움 중간 (멀티스레드 필요) 복잡

7. I/O Software Layers

Goals of I/O Software — I/O 소프트웨어 목표:

  • Device independence: 장치 종류와 무관한 동일 인터페이스
  • Uniform naming: 균일한 이름 체계
  • Error handling: 오류 처리
  • Synchronous vs. asynchronous: 동기·비동기 지원
  • Buffering: 버퍼링
  • Sharable vs. dedicated devices: 공유·전용 장치 구분

I/O 소프트웨어는 아래부터 위로 다음 계층으로 구성된다.

I/O Software Layers 개요
계층 (아래 → 위) 주요 역할 세부 사항
Hardware 실제 I/O 장치 동작 컨트롤러, 레지스터, 버스
Interrupt Handlers 인터럽트 수신·처리 장치별 ISR; 완료 시 상위 드라이버 깨움
Device Drivers 장치별 제어 코드 정적 링크·부팅 시 로드·동적 로드 가능; Linux 커널 코드의 70%; Windows XP 충돌의 85% 원인
Device-Independent I/O Software 드라이버에 균일한 인터페이스, 오류 보고, 버퍼링 Unix: 장치를 특수 파일로 모델링; open(), read(), write(), ioctl() 등; major/minor 장치 번호; /dev 디렉터리
User-Space I/O Software 사용자 수준 라이브러리 제공 C 표준 I/O 라이브러리(fopen(), fgets(), fscanf()); 사용자 정의 라이브러리 작성 가능

Device Drivers — 신뢰성 문제

device driver는 OS 안정성의 주요 위협 요소이다.

  • Windows 시스템의 5%가 매일 충돌 — 주식 거래소, 전자상거래 등 막대한 비용
  • Linux 커널 코드의 70%가 드라이버
  • Windows XP에 35,000개 이상의 드라이버, 120,000개 이상의 버전
  • 드라이버는 커널보다 7배 더 버그가 많음 (Linux 기준)
  • Windows XP 충돌의 85%가 드라이버 원인
  • 경험이 부족한 프로그래머가 작성하는 경우가 많음

Device-Independent I/O Software — 오류 처리 방식

  • 시스템 콜에 오류 코드를 담아 반환
  • 일정 횟수 재시도
  • 오류 무시
  • 호출 프로세스 종료
  • 시스템 전체 종료

User-Space I/O Software

user-space I/O software는 라이브러리 형태로 제공된다. C 표준 I/O 라이브러리의 fopen()과 시스템 콜 open()의 차이처럼 사용자는 어떤 라이브러리 함수를 쓸지 선택할 수 있고, 필요하면 자신만의 I/O 라이브러리 (myopen(), myfgets() 등)를 만들 수도 있다.

참고 I/O Systems Layers 요약: 사용자 요청은 user-space 라이브러리에서 시작해 device-independent 계층, device driver, interrupt handler를 거쳐 하드웨어에 도달하고, 완료 통지는 반대 방향으로 전달된다.

예상 서술형문제

Q1 interrupt-driven I/O의 동작 과정을 단계별로 서술하고, polling 방식과 비교하여 각각의 장단점을 설명하시오.

모범답안 보기

Interrupt-driven I/O 동작 과정: ① CPU가 장치에 I/O 명령을 내린다. ② CPU는 I/O 완료를 기다리지 않고 다른 작업을 계속 수행한다. ③ 장치가 작업을 마치면 interrupt 신호를 CPU에 전송한다. ④ CPU는 현재 수행 중인 작업의 컨텍스트를 저장하고, 해당 장치의 interrupt service routine(ISR)으로 분기한다. ⑤ ISR이 I/O 결과를 처리한 후 반환되면 CPU는 이전 작업으로 복귀한다.

Polling과의 비교:

항목 polling interrupt-driven I/O
CPU 활용 장치 상태 확인에 CPU 낭비 필요할 때만 응답, 일반적으로 효율적
구현 복잡도 단순 ISR 구현 필요
빠른 장치 효율적 (응답이 바로 옴) 오버헤드 발생 가능
느린 장치 CPU 낭비 심함 효율적
참고 과도한 인터럽트는 프로그램 실행을 방해하거나 막을 수 있으며, 바이트당 1회 인터럽트 발생 시 오버헤드가 크다.

Q2 DMAprogrammed I/O의 차이를 설명하고, DMA가 효과적인 상황과 그 이유를 서술하시오.

모범답안 보기

Programmed I/O(PIO): 데이터 전송 시 CPU가 직접 I/O 장치와 메모리 사이에서 데이터를 옮기는 방식이다. 전용 I/O 명령 또는 memory-mapped I/O를 통해 구현하며, CPU가 전송 내내 관여하므로 효율이 낮다.

DMA(Direct Memory Access): 별도의 DMA controller가 CPU를 우회하여 장치 버퍼에서 주 메모리로(또는 반대로) 데이터 블록을 직접 전송하는 방식이다. CPU는 전송 시작 명령만 내리고, 블록 전송이 완료될 때 단 한 번의 인터럽트를 받는다.

DMA가 효과적인 상황: 메모리 속도에 근접한 고속 I/O 장치(디스크, 네트워크 카드 등)에서 대용량 데이터를 전송할 때 적합하다. PIO 방식으로 같은 데이터를 전송하면 CPU가 오랫동안 전송에만 묶여 다른 작업을 할 수 없으나, DMA를 쓰면 CPU는 전송 중에도 다른 프로세스를 실행할 수 있어 시스템 전체 처리량이 높아진다.

Q3 memory-mapped I/Odirect I/O의 차이를 설명하고, memory-mapped I/O의 장점을 구체적으로 서술하시오.

모범답안 보기

Direct I/O(포트 I/O): Intel의 in, out 같은 전용 I/O 명령어를 사용하여 별도의 I/O 포트 주소 공간에 접근하는 방식이다.

Memory-mapped I/O: 장치 컨트롤러 레지스터를 프로세서의 메모리 주소 공간에 매핑하여, 일반 load/store 명령으로 I/O를 수행하는 방식이다.

Memory-mapped I/O의 장점: ① 장치 드라이버를 C로 완전히 작성할 수 있어 개발 생산성이 높다. ② 특별한 보호 메커니즘이 불필요하며, PTE를 통해 보호하므로 기존 메모리 보호 체계를 재사용한다. ③ 페이지 테이블에 해당 페이지를 포함시켜 특정 사용자 프로세스가 특정 장치를 직접 제어하도록 권한을 세밀하게 부여할 수 있다. ④ 레지스터 읽기와 값 테스트를 단일 명령으로 처리할 수 있어 효율적이다.

Q4 blocking I/O, non-blocking I/O, asynchronous I/O의 차이를 비교하여 설명하시오.

모범답안 보기

Blocking I/O: read(), write() 등 I/O 시스템 콜을 호출하면 I/O가 완료될 때까지 프로세스가 대기(suspend) 상태가 된다. 사용과 이해가 단순하다는 장점이 있으나, I/O 중에 CPU를 다른 작업에 활용하지 못한다.

Non-blocking I/O: I/O 호출이 즉시 반환되며, 실제로 전송된 바이트 수를 반환 값으로 전달한다. 멀티스레딩으로 구현하고, select()로 데이터 준비 여부를 확인한다. 프로세스는 I/O 중에도 다른 작업을 계속할 수 있다.

Asynchronous I/O: I/O 요청 후 프로세스가 계속 실행되다가, I/O가 완료되면 운영체제가 시그널·콜백 등의 방식으로 완료를 통지한다. 완료 전까지는 얼마나 전송됐는지 알 수 없다. non-blocking I/O와 달리 완료 통지가 운영체제에서 자동으로 이루어진다.

Q5 I/O 소프트웨어 계층 구조를 아래부터 위의 순서로 나열하고, 각 계층의 역할을 설명하시오.

모범답안 보기

I/O 소프트웨어 계층은 아래부터 위로 다음과 같다.

Hardware: 실제 I/O 장치. 컨트롤러, 레지스터, 버스를 통해 데이터를 전송한다.

Interrupt Handlers: 장치가 발생시킨 인터럽트를 수신하고 처리한다. 해당 device driver를 깨운다.

Device Drivers: 각 I/O 장치를 제어하는 장치별 코드. 장치 독립적 I/O 소프트웨어 및 인터럽트 핸들러와 상호작용하며, 커널에 정적 링크되거나 부팅 시·실행 중 동적으로 로드된다.

Device-Independent I/O Software: 모든 드라이버에 균일한 인터페이스를 제공한다. Unix에서는 장치를 특수 파일(/dev)로 모델링하며, open(), read(), write(), ioctl() 등의 시스템 콜로 접근한다. 오류 보고, 버퍼링, major/minor 장치 번호를 통한 드라이버 탐색을 담당한다.

User-Space I/O Software: 사용자 수준 라이브러리(C 표준 I/O 등)를 제공한다. fopen(), fgets(), fscanf() 등이 해당되며, 사용자가 직접 I/O 라이브러리를 만들 수도 있다.

Q6 device driver가 운영체제 안정성에 미치는 영향을 설명하고, 드라이버를 시스템에 탑재하는 세 가지 방식을 서술하시오.

모범답안 보기

운영체제 안정성에 미치는 영향: device driver는 OS 안정성의 주요 위협 요소이다. Windows XP 충돌의 85%가 드라이버에 의한 것이며, Linux에서 드라이버는 커널보다 7배 더 버그가 많다. 드라이버는 Linux 커널 코드의 70%를 차지하고, Windows XP에만 35,000개 이상(120,000개 이상의 버전)이 존재한다. 경험이 부족한 프로그래머가 작성하는 경우가 많아 품질 편차가 크고, 디지털 가전·임베디드 장치의 증가로 관리되지 않는 시스템의 드라이버 문제도 커지고 있다.

드라이버 탑재 방식 세 가지:정적 링크(Statically linked with the kernel): 드라이버를 커널과 함께 컴파일·링크하여 커널 이미지에 포함시킨다. ② 부팅 시 선택 로드(Selectively loaded during boot time): 시스템 부팅 시 필요한 드라이버만 선택하여 로드한다. ③ 실행 중 동적 로드(Dynamically loaded during execution): 실행 도중 필요 시 로드하며, 특히 핫플러그 장치(hot pluggable devices)에 적합하다.

Q7 Unix에서 device-independent I/O software가 장치를 특수 파일로 다루는 방식을 설명하고, major 장치 번호와 minor 장치 번호의 역할을 서술하시오.

모범답안 보기

Unix에서 device-independent I/O software는 장치를 특수 파일(special file)로 모델링한다. 이를 통해 open(), read(), write(), close(), ioctl() 등 일반 파일에 쓰이는 시스템 콜로 장치에 접근할 수 있다. 각 장치에는 파일 이름이 부여되며(/dev 디렉터리), 일반 파일과 동일한 보호 규칙(권한 비트 등)이 적용된다.

Major 장치 번호: 어떤 장치 드라이버를 사용할지 결정하는 데 쓰인다. 커널이 major 번호를 보고 해당 드라이버를 찾는다.

Minor 장치 번호: i-node에 저장되어 드라이버에 매개변수로 전달된다. 같은 종류의 장치 중 구체적으로 어느 유닛(unit)을 대상으로 하는지를 드라이버가 식별할 수 있게 한다.

Q8 block devicecharacter device의 차이를 비교하고, 각각에 해당하는 장치의 예를 들어 설명하시오.

모범답안 보기

Block device: 고정 크기(512B–32KB)의 블록 단위로 데이터를 저장한다. 각 블록에 고유한 주소가 있어 임의 접근(random access)이 가능하고, 다른 블록과 독립적으로 읽기·쓰기를 수행할 수 있다. 대표적인 장치로 하드 디스크, 테이프 등이 있다.

Character device: 바이트 스트림을 순서대로 전달하거나 받는 방식으로 동작한다. 주소가 없으므로 임의 접근 및 seek 연산이 불가능하다. 대표적인 장치로 프린터, 모뎀, 마우스, 키보드 등이 있다.

핵심 차이는 주소 지정 가능 여부와 임의 접근 가능 여부이다. block device는 파일 시스템 구현에 적합하고, character device는 스트림 기반 입출력에 적합하다.