입출력 시스템

입출력 시스템

개요

입출력(I/O) 시스템은 CPU와 외부 장치(키보드, 디스크, 네트워크 등) 간의 데이터 전송을 담당합니다. I/O 시스템의 설계는 시스템 전체 성능에 큰 영향을 미치며, 폴링, 인터럽트, DMA 등 다양한 방식으로 구현됩니다. 이 레슨에서는 I/O 시스템의 구조와 동작 원리를 학습합니다.

난이도: ⭐⭐⭐

선수 지식: CPU 구조, 메모리 시스템


목차

  1. I/O 시스템 개요
  2. 프로그램 I/O (폴링)
  3. 인터럽트 기반 I/O
  4. DMA (Direct Memory Access)
  5. 버스 구조
  6. I/O 인터페이스
  7. 현대 I/O 시스템
  8. 연습 문제

1. I/O 시스템 개요

1.1 I/O 장치의 다양성

┌─────────────────────────────────────────────────────────────┐
│                  I/O 장치 분류                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  입력 장치                출력 장치              저장 장치   │
│  ┌──────────┐           ┌──────────┐          ┌──────────┐ │
│  │ 키보드   │           │ 모니터   │          │ HDD/SSD  │ │
│  │ 마우스   │           │ 프린터   │          │ USB      │ │
│  │ 스캐너   │           │ 스피커   │          │ SD카드   │ │
│  │ 마이크   │           │ LED      │          │ 광학드라이브│ │
│  │ 터치스크린│           │          │          │          │ │
│  └──────────┘           └──────────┘          └──────────┘ │
│                                                             │
│  통신 장치                                                  │
│  ┌────────────────────────────────────────────────────────┐│
│  │ 네트워크 카드 (NIC), WiFi, Bluetooth, USB 허브          ││
│  └────────────────────────────────────────────────────────┘│
│                                                             │
└─────────────────────────────────────────────────────────────┘

I/O 장치 특성:
┌─────────────────┬──────────────┬───────────────────────────┐
│     장치        │   데이터율    │         특성              │
├─────────────────┼──────────────┼───────────────────────────┤
│ 키보드          │  ~100 B/s    │ 저속, 비동기, 문자 단위   │
├─────────────────┼──────────────┼───────────────────────────┤
│ 마우스          │  ~1 KB/s     │ 저속, 동기, 이벤트        │
├─────────────────┼──────────────┼───────────────────────────┤
│ 기가비트 이더넷  │  125 MB/s    │ 고속, 패킷 단위           │
├─────────────────┼──────────────┼───────────────────────────┤
│ SATA SSD       │  ~600 MB/s   │ 고속, 블록 단위           │
├─────────────────┼──────────────┼───────────────────────────┤
│ NVMe SSD       │  ~7 GB/s     │ 초고속, 병렬 처리         │
├─────────────────┼──────────────┼───────────────────────────┤
│ 4K 디스플레이   │  ~20 GB/s    │ 초고속, 연속 데이터       │
└─────────────────┴──────────────┴───────────────────────────┘

1.2 I/O 시스템 구조

┌─────────────────────────────────────────────────────────────┐
│                    I/O 시스템 계층                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────────────────────────────────────────────┐   │
│   │              응용 프로그램                            │   │
│   │         read(), write(), printf()                   │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐   │
│   │            운영체제 I/O 서브시스템                     │   │
│   │      버퍼링, 캐싱, 스풀링, 스케줄링                    │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐   │
│   │              디바이스 드라이버                         │   │
│   │        장치별 제어 코드, 인터럽트 처리                  │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐   │
│   │              하드웨어 컨트롤러                         │   │
│   │        I/O 포트, 레지스터, 버스 인터페이스             │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐   │
│   │               I/O 장치                               │   │
│   │          물리적 장치 (디스크, 키보드 등)               │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.3 I/O 제어 방식 비교

┌───────────────────┬────────────────────┬────────────────────┐
│                   │  프로그램 I/O      │   인터럽트 I/O     │
│      특성         │    (폴링)          │                    │
├───────────────────┼────────────────────┼────────────────────┤
│ CPU 개입          │      높음          │       중간         │
├───────────────────┼────────────────────┼────────────────────┤
│ CPU 효율          │      낮음          │       높음         │
├───────────────────┼────────────────────┼────────────────────┤
│ 구현 복잡도       │      낮음          │       중간         │
├───────────────────┼────────────────────┼────────────────────┤
│ 적합한 장치       │   고속, 예측 가능  │  저속, 비동기      │
├───────────────────┼────────────────────┼────────────────────┤
│ 데이터 전송       │  CPU 경유          │   CPU 경유         │
└───────────────────┴────────────────────┴────────────────────┘

┌───────────────────┬────────────────────┐
│                   │       DMA          │
│      특성         │                    │
├───────────────────┼────────────────────┤
│ CPU 개입          │       낮음         │
├───────────────────┼────────────────────┤
│ CPU 효율          │     매우 높음      │
├───────────────────┼────────────────────┤
│ 구현 복잡도       │       높음         │
├───────────────────┼────────────────────┤
│ 적합한 장치       │   대용량 전송      │
├───────────────────┼────────────────────┤
│ 데이터 전송       │   메모리 직접      │
└───────────────────┴────────────────────┘

2. 프로그램 I/O (폴링)

2.1 폴링의 개념

정의: CPU가 I/O 장치의 상태를 주기적으로 확인하며 데이터 전송

┌─────────────────────────────────────────────────────────────┐
                    폴링 동작 과정                            
├─────────────────────────────────────────────────────────────┤
                                                             
      CPU                           I/O 장치                 
                                                           
         1. 상태 확인 (읽기 준비?)                          
       │──────────────────────────────▶│                     
                                                           
         2. "아직 안 됨"                                    
       │◀──────────────────────────────│                     
                                                           
         3. 상태 확인 (다시)                                
       │──────────────────────────────▶│                     
                                                           
         4. "아직 안 됨"                                    
       │◀──────────────────────────────│                     
                ...반복...                                  
         N. 상태 확인                                       
       │──────────────────────────────▶│                     
                                                           
         N+1. "준비 완료"                                   
       │◀──────────────────────────────│                     
                                                           
         N+2. 데이터 읽기                                   
       │◀──────────────────────────────│                     
                                                           
                                                             
   문제: CPU가 대기하며 아무 일도 못함 (Busy Waiting)          
                                                             
└─────────────────────────────────────────────────────────────┘

2.2 I/O 포트와 레지스터

I/O 장치 컨트롤러의 레지스터:

┌─────────────────────────────────────────────────────────────┐
│              I/O Controller Registers                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ Status Register (상태 레지스터)                      │    │
│  │ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐  │    │
│  │ │Busy │Ready│Error│ IRQ │ ... │ ... │ ... │ ... │  │    │
│  │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘  │    │
│  │ - 장치 상태 표시 (읽기 전용)                         │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ Control Register (제어 레지스터)                     │    │
│  │ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐  │    │
│  │ │Start│ IE  │Mode │ Dir │ ... │ ... │ ... │ ... │  │    │
│  │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘  │    │
│  │ - 장치 제어 명령 (쓰기)                              │    │
│  │ - IE: Interrupt Enable                              │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │ Data Register (데이터 레지스터)                      │    │
│  │ ┌───────────────────────────────────────────────┐   │    │
│  │ │                Data (8/16/32 bits)            │   │    │
│  │ └───────────────────────────────────────────────┘   │    │
│  │ - 실제 전송 데이터                                   │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3 폴링 프로그래밍 예시

// 간단한 폴링 기반 I/O 코드

#define STATUS_REG  0x3F8  // 상태 레지스터 주소
#define DATA_REG    0x3F9  // 데이터 레지스터 주소
#define READY_BIT   0x01   // Ready 비트 마스크

// 문자 출력 (폴링)
void putchar_polling(char c) {
    // 장치가 준비될 때까지 대기 (Busy Wait)
    while ((inb(STATUS_REG) & READY_BIT) == 0) {
        // CPU가 계속 루프를 돌며 확인
        // 아무 일도 하지 못함
    }

    // 데이터 전송
    outb(DATA_REG, c);
}

// 문자열 출력
void print_string_polling(const char* str) {
    while (*str) {
        putchar_polling(*str++);
    }
}

// 문자 입력 (폴링)
char getchar_polling(void) {
    // 입력 데이터가 있을 때까지 대기
    while ((inb(STATUS_REG) & READY_BIT) == 0) {
        // Busy Wait
    }

    return inb(DATA_REG);
}

2.4 폴링의 장단점

장점:
┌─────────────────────────────────────────────────────────────┐
│ - 구현이 간단함                                              │
│ - 하드웨어 요구사항 최소                                     │
│ - 예측 가능한 타이밍                                         │
│ - 빠른 장치에서는 효율적 (데이터가 즉시 준비되는 경우)         │
│ - 실시간 시스템에서 지터(jitter) 최소화                       │
└─────────────────────────────────────────────────────────────┘

단점:
┌─────────────────────────────────────────────────────────────┐
│ - CPU 시간 낭비 (Busy Waiting)                              │
│ - 느린 장치에서 매우 비효율적                                │
│ - 여러 장치 처리 어려움                                      │
│ - 전력 소비 증가                                            │
└─────────────────────────────────────────────────────────────┘

CPU 시간 낭비 계산:
- 시리얼 포트: 115200 bps = 11520 문자/초
- 한 문자당 약 87us
- 3GHz CPU: 87us = 261,000 사이클
- 한 문자 전송에 CPU가 26만 사이클 대기!

3. 인터럽트 기반 I/O

3.1 인터럽트의 개념

정의: I/O 장치가 CPU에 비동기적으로 신호를 보내 처리를 요청

┌─────────────────────────────────────────────────────────────┐
                  인터럽트 동작 과정                           
├─────────────────────────────────────────────────────────────┤
                                                             
      CPU (작업 수행 )                    I/O 장치         
                                                           
         1. I/O 명령 발행                                  
       │─────────────────────────────────────▶│              
                                                           
         2. 다른 작업 수행                                 
         (I/O 완료 기다리지 않음)                          
                                                           
                ...시간 경과...               3. I/O 처리  
                                                           
         4. 인터럽트 신호 (IRQ)                            
       │◀═════════════════════════════════════│              
                                                           
         5. 현재 상태 저장                                 
         6. 인터럽트 핸들러 실행                           
         7. 데이터 전송                                    
         8. 원래 작업 재개                                 
                                                           
                                                             
   장점: CPU가 I/O 대기 중에도 다른 작업 수행 가능             
                                                             
└─────────────────────────────────────────────────────────────┘

3.2 인터럽트 처리 과정

┌─────────────────────────────────────────────────────────────┐
│               인터럽트 처리 상세 단계                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 장치가 IRQ 라인 활성화                                   │
│     └── 인터럽트 컨트롤러 (PIC/APIC) 신호 전달             │
│                                                             │
│  2. 인터럽트 컨트롤러가 CPU 인터럽트 요청                   │
│     └── 인터럽트 우선순위 확인                               │
│     └── 마스크되지 않은 경우 CPU 전달                      │
│                                                             │
│  3. CPU 현재 명령어 완료  인터럽트 확인                   │
│     └── 인터럽트 플래그 확인 (IF)                            │
│     └── 인터럽트 가능 상태면 처리 시작                        │
│                                                             │
│  4. CPU 상태 저장                                           │
│     └── 플래그 레지스터  스택                               │
│     └── CS:IP (또는 RIP)  스택                             │
│     └── 인터럽트 비활성화 (중첩 방지)                        │
│                                                             │
│  5. 인터럽트 벡터 테이블 참조                                │
│     └── 인터럽트 번호로 핸들러 주소 조회                     │
│                                                             │
│  6. 인터럽트 서비스 루틴 (ISR) 실행                          │
│     └── 장치별 처리 코드 실행                                │
│     └── 인터럽트 확인 (Acknowledge) 신호 전송                │
│                                                             │
│  7. EOI (End of Interrupt) 전송                             │
│     └── 인터럽트 컨트롤러에 처리 완료 알림                   │
│                                                             │
│  8. IRET 명령으로 복귀                                      │
│     └── 저장된 상태 복원                                     │
│     └── 원래 코드로 복귀                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.3 인터럽트 벡터 테이블

x86 인터럽트 벡터 테이블 (IDT):

┌─────────────────────────────────────────────────────────────┐
│             Interrupt Descriptor Table (IDT)                 │
├──────┬──────────────────────────────────────────────────────┤
│ Vec  │ 설명                                                  │
├──────┼──────────────────────────────────────────────────────┤
│  0   │ Divide Error (#DE)                                   │
│  1   │ Debug Exception (#DB)                                │
│  2   │ NMI (Non-Maskable Interrupt)                         │
│  3   │ Breakpoint (#BP)                                     │
│  6   │ Invalid Opcode (#UD)                                 │
│  8   │ Double Fault (#DF)                                   │
│ 13   │ General Protection Fault (#GP)                       │
│ 14   │ Page Fault (#PF)                                     │
│ ...  │ ...                                                  │
│ 32   │ IRQ 0: Timer (PIT)                                   │
│ 33   │ IRQ 1: Keyboard                                      │
│ 34   │ IRQ 2: Cascade (PIC2 연결)                           │
│ 35   │ IRQ 3: COM2/COM4                                     │
│ 36   │ IRQ 4: COM1/COM3                                     │
│ ...  │ ...                                                  │
│ 46   │ IRQ 14: Primary IDE                                  │
│ 47   │ IRQ 15: Secondary IDE                                │
│ ...  │ ...                                                  │
│ 128  │ System Call (Linux: int 0x80)                        │
│ ...  │ ...                                                  │
│ 255  │ Reserved                                             │
└──────┴──────────────────────────────────────────────────────┘

IDT Entry 구조 (64비트):
┌─────────────────────────────────────────────────────────────┐
│  63        48 47 46  44 43    40 39  35 34  32             │
│  ├───────────┼──┼──────┼────────┼─────┼─────┤              │
│  │  Offset   │P │ DPL  │  Type  │ IST │  0  │              │
│  │   [63:48] │  │      │        │     │     │              │
│  └───────────┴──┴──────┴────────┴─────┴─────┘              │
│  31              16 15                0                     │
│  ├─────────────────┼─────────────────┤                     │
│  │  Segment Sel.   │  Offset [15:0]  │                     │
│  └─────────────────┴─────────────────┘                     │
└─────────────────────────────────────────────────────────────┘

3.4 인터럽트 기반 I/O 프로그래밍

// 인터럽트 기반 키보드 드라이버 예시

#define KEYBOARD_IRQ    1
#define KEYBOARD_PORT   0x60

// 키보드 버퍼
volatile char keyboard_buffer[256];
volatile int buffer_head = 0;
volatile int buffer_tail = 0;

// 인터럽트 핸들러 (ISR)
void keyboard_handler(void) {
    // 1. 스캔코드 읽기
    unsigned char scancode = inb(KEYBOARD_PORT);

    // 2. 버퍼에 저장
    keyboard_buffer[buffer_head] = scancode;
    buffer_head = (buffer_head + 1) % 256;

    // 3. EOI 전송 (인터럽트 완료 알림)
    outb(0x20, 0x20);  // PIC에 EOI
}

// 문자 읽기 (블로킹)
char getchar_interrupt(void) {
    // 버퍼에 데이터가 있을 때까지 대기
    // (실제로는 sleep/wakeup 사용)
    while (buffer_tail == buffer_head) {
        // CPU 절전 또는 다른 프로세스 실행
        asm("hlt");  // Halt until interrupt
    }

    char c = keyboard_buffer[buffer_tail];
    buffer_tail = (buffer_tail + 1) % 256;
    return c;
}

// 인터럽트 핸들러 등록
void init_keyboard(void) {
    // IDT에 핸들러 등록
    set_interrupt_handler(32 + KEYBOARD_IRQ, keyboard_handler);

    // 인터럽트 활성화
    enable_irq(KEYBOARD_IRQ);
}

3.5 인터럽트의 장단점

장점:
┌─────────────────────────────────────────────────────────────┐
│ - CPU 효율성 향상 (대기 시간에 다른 작업 수행)               │
│ - 비동기 이벤트 처리에 적합                                  │
│ - 다중 장치 처리 용이                                       │
│ - 전력 효율적 (CPU가 대기 상태로 전환 가능)                  │
└─────────────────────────────────────────────────────────────┘

단점:
┌─────────────────────────────────────────────────────────────┐
│ - 구현 복잡성 증가                                          │
│ - 인터럽트 오버헤드 (컨텍스트 저장/복원)                     │
│ - 인터럽트 지연 시간 (Latency) 존재                         │
│ - 고빈도 인터럽트 시 오버헤드 증가 (Interrupt Storm)        │
└─────────────────────────────────────────────────────────────┘

인터럽트 오버헤드:
- 상태 저장: ~100 사이클
- 핸들러 진입: ~50 사이클
- 캐시/TLB 영향: ~100+ 사이클
- 총: ~500-1000 사이클/인터럽트

초당 10만 인터럽트 @ 3GHz:
오버헤드 = 100,000 × 500 / 3,000,000,000 ≈ 1.7% CPU

4. DMA (Direct Memory Access)

4.1 DMA의 개념

정의: CPU를 거치지 않고 I/O 장치와 메모리  직접 데이터 전송

┌─────────────────────────────────────────────────────────────┐
              DMA vs CPU 기반 전송 비교                       
├─────────────────────────────────────────────────────────────┤
                                                             
  CPU 기반 전송 (Programmed I/O):                            
                                                             
    Memory ←──── CPU ────→ I/O Device                        
           read      write                                   
                                                             
    -  바이트마다 CPU 개입                                  
    - CPU가 데이터 이동 담당                                  
    - CPU 시간 소모                                         
                                                             
  DMA 전송:                                                  
                                                             
    Memory ◀══════════════▶ I/O Device                       
                                                            
               DMA Controller                               
              └─────────┐                                    
                                                            
           CPU ─────────┘ (설정만)                           
                                                             
    - CPU는 전송 설정만 담당                                  
    - DMA 컨트롤러가 전송 수행                                
    - 전송 완료  인터럽트로 알림                            
    - CPU는 다른 작업 가능                                    
                                                             
└─────────────────────────────────────────────────────────────┘

4.2 DMA 동작 과정

┌─────────────────────────────────────────────────────────────┐
│                    DMA 전송 과정                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. CPU가 DMA 컨트롤러 설정                                  │
│     ┌─────────────────────────────────────────────────────┐│
│     │ - 소스 주소 (메모리 또는 I/O)                        ││
│     │ - 목적지 주소                                        ││
│     │ - 전송 크기 (바이트 수)                              ││
│     │ - 전송 방향 (읽기/쓰기)                              ││
│     │ - 전송 모드 (블록/사이클 스틸링)                      ││
│     └─────────────────────────────────────────────────────┘│
│                                                             │
│  2. CPU가 DMA 전송 시작 명령                                 │
│                                                             │
│  3. DMA 컨트롤러가 버스 요청 (Bus Request)                   │
│     - CPU에 HOLD 신호 전송                                  │
│                                                             │
│  4. CPU가 버스 승인 (Bus Grant)                             │
│     - HLDA 신호로 버스 제어권 양도                          │
│     - CPU는 버스 사용하지 않는 작업 수행                    │
│                                                             │
│  5. DMA 컨트롤러가 데이터 전송                              │
│     ┌────────┐         ┌────────┐                          │
│     │ Memory │◀═══════▶│  I/O   │                          │
│     └────────┘   DMA   └────────┘                          │
│                  Bus                                        │
│                                                             │
│  6. 전송 완료 시                                            │
│     - 버스 반환                                             │
│     - CPU에 인터럽트 발생                                   │
│                                                             │
│  7. CPU가 완료 처리                                         │
│     - 상태 확인                                             │
│     - 필요시 다음 전송 설정                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.3 DMA 컨트롤러 구조

┌─────────────────────────────────────────────────────────────┐
│                    DMA Controller                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              Control Registers                       │    │
│  ├──────────────────────────────────────────────────────┤    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │ Command Register                             │    │    │
│  │  │ - DMA 동작 모드 설정                          │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │ Mode Register                                │    │    │
│  │  │ - 전송 방향, 모드 (단일/블록/수요)            │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │ Status Register                              │    │    │
│  │  │ - 완료 상태, 요청 상태                        │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              Channel 0                              │    │
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐   │    │
│  │  │ Address Reg │ │ Count Reg   │ │ Page Reg    │   │    │
│  │  │  0x0000     │ │   1024      │ │   0x00      │   │    │
│  │  └─────────────┘ └─────────────┘ └─────────────┘   │    │
│  └──────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              Channel 1                              │    │
│  │  ... (동일 구조)                                    │    │
│  └──────────────────────────────────────────────────────┘    │
│  ... Channel 2, 3, ...                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.4 DMA 전송 모드

1. 블록 전송 (Block/Burst Mode):
┌─────────────────────────────────────────────────────────────┐
                                                             
  ──────┬────────────────────────────────┬──────             
   CPU             DMA                   CPU               
  사용       버스 독점 사용              사용              
  ──────┴────────────────────────────────┴──────             
                                                             
  - 전체 블록을  번에 전송                                  
  - 가장 빠른 전송                                           
  - CPU가 오래 대기할  있음                                 
  - 대용량 데이터에 적합                                      
                                                             
└─────────────────────────────────────────────────────────────┘

2. 사이클 스틸링 (Cycle Stealing):
┌─────────────────────────────────────────────────────────────┐
                                                             
  ──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──                          
  C D C D C D C D C D C                           
  P M P M P M P M P M P                           
  U A U A U A U A U A U                           
  ──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──                          
                                                             
  -  번에  워드/바이트씩 전송                             
  - CPU와 DMA가 번갈아 버스 사용                             
  - CPU 영향 최소화                                          
  - 전송 속도는 느림                                         
                                                             
└─────────────────────────────────────────────────────────────┘

3. 수요 전송 (Demand Mode):
┌─────────────────────────────────────────────────────────────┐
                                                             
  장치가 준비될 때마다 전송 (DREQ 신호 기반)                  
  장치 속도에 맞춤                                           
                                                             
└─────────────────────────────────────────────────────────────┘

4.5 DMA 프로그래밍 예시

// DMA 디스크 읽기 예시 (간략화)

#define DMA_CHANNEL     2
#define DMA_ADDR_REG    0x04  // 채널 2 주소
#define DMA_COUNT_REG   0x05  // 채널 2 카운트
#define DMA_PAGE_REG    0x81  // 채널 2 페이지
#define DMA_MODE_REG    0x0B
#define DMA_MASK_REG    0x0A

// DMA 전송 설정
void setup_dma_read(void* buffer, size_t count) {
    uint32_t addr = (uint32_t)buffer;

    // 1. DMA 채널 마스크 (비활성화)
    outb(DMA_MASK_REG, DMA_CHANNEL | 0x04);

    // 2. 플립플롭 리셋
    outb(0x0C, 0);

    // 3. 모드 설정 (읽기, 채널 2, 단일 모드)
    outb(DMA_MODE_REG, 0x46);

    // 4. 주소 설정 (하위 16비트)
    outb(DMA_ADDR_REG, addr & 0xFF);
    outb(DMA_ADDR_REG, (addr >> 8) & 0xFF);

    // 5. 페이지 설정 (상위 비트)
    outb(DMA_PAGE_REG, (addr >> 16) & 0xFF);

    // 6. 카운트 설정 (count - 1)
    outb(DMA_COUNT_REG, (count - 1) & 0xFF);
    outb(DMA_COUNT_REG, ((count - 1) >> 8) & 0xFF);

    // 7. DMA 채널 활성화
    outb(DMA_MASK_REG, DMA_CHANNEL);
}

// 디스크 읽기 명령 + DMA
void read_disk_dma(void* buffer, uint32_t sector, uint16_t count) {
    // DMA 설정
    setup_dma_read(buffer, count * 512);

    // 디스크 컨트롤러에 읽기 명령 발행
    issue_disk_read_command(sector, count);

    // CPU는 다른 작업 수행 가능
    // 전송 완료 시 인터럽트 발생
}

// DMA 완료 인터럽트 핸들러
void dma_complete_handler(void) {
    // 전송 완료 처리
    // 상태 확인, 버퍼 사용 가능
    signal_dma_complete();
}

5. 버스 구조

5.1 버스의 종류

┌─────────────────────────────────────────────────────────────┐
│                    시스템 버스 구조                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                      CPU                             │    │
│  └─────────────────────────┬───────────────────────────┘    │
│                            │                                │
│                       Front-Side Bus                        │
│                      (또는 QPI/UPI)                         │
│                            │                                │
│  ┌─────────────────────────┴───────────────────────────┐    │
│  │              Memory Controller Hub                   │    │
│  │                   (Northbridge)                      │    │
│  └───────────┬─────────────┬─────────────┬─────────────┘    │
│              │             │             │                  │
│          Memory         PCIe         DMI                   │
│           Bus           x16                                │
│              │             │             │                  │
│  ┌───────────┴───┐    ┌────┴────┐       │                  │
│  │    DRAM       │    │   GPU   │       │                  │
│  └───────────────┘    └─────────┘       │                  │
│                                         │                  │
│  ┌──────────────────────────────────────┴──────────────┐   │
│  │             I/O Controller Hub                      │   │
│  │                 (Southbridge)                       │   │
│  └───────┬──────────┬──────────┬──────────┬───────────┘   │
│          │          │          │          │                │
│        SATA       USB        PCIe      Audio              │
│          │          │        x1         │                  │
│    ┌─────┴─────┐ ┌──┴──┐  ┌──┴──┐  ┌───┴───┐             │
│    │ HDD/SSD  │ │ USB │  │ NIC │  │Codec  │             │
│    └──────────┘ │Devs │  └─────┘  └───────┘             │
│                 └─────┘                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.2 버스 특성

버스 구성 요소:
┌─────────────────────────────────────────────────────────────┐
│  ┌───────────────────────────────────────────────────────┐  │
│  │                    Data Bus                            │  │
│  │  - 데이터 전송 라인                                     │  │
│  │  - 폭: 8, 16, 32, 64비트                               │  │
│  │  - 양방향                                              │  │
│  └───────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                   Address Bus                          │  │
│  │  - 메모리/I/O 주소 지정                                 │  │
│  │  - 폭: 20, 32, 36, 40+비트                             │  │
│  │  - 단방향 (CPU → 장치)                                  │  │
│  └───────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────┐  │
│  │                   Control Bus                          │  │
│  │  - 제어 신호 전송                                       │  │
│  │  - Read, Write, IRQ, DMA 요청 등                       │  │
│  │  - 양방향                                              │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

주요 버스 표준:
┌────────────────┬──────────────┬───────────────┬─────────────┐
│     버스        │   대역폭      │    용도        │    특징     │
├────────────────┼──────────────┼───────────────┼─────────────┤
│ PCIe 4.0 x16   │  ~64 GB/s    │ GPU, 고속 장치 │ 직렬, 레인  │
├────────────────┼──────────────┼───────────────┼─────────────┤
│ PCIe 5.0 x16   │  ~128 GB/s   │ 차세대 GPU    │ 최신 표준   │
├────────────────┼──────────────┼───────────────┼─────────────┤
│ SATA III       │  ~600 MB/s   │ 저장 장치      │ 직렬        │
├────────────────┼──────────────┼───────────────┼─────────────┤
│ NVMe (PCIe 4)  │  ~7 GB/s     │ 고속 SSD      │ 저지연      │
├────────────────┼──────────────┼───────────────┼─────────────┤
│ USB 3.2 Gen2  │  ~1.25 GB/s  │ 주변 장치      │ 범용        │
├────────────────┼──────────────┼───────────────┼─────────────┤
│ Thunderbolt 4 │  ~5 GB/s     │ 고속 주변장치  │ 데이지체인  │
└────────────────┴──────────────┴───────────────┴─────────────┘

5.3 버스 중재

여러 장치가 버스를 공유할 때 충돌 방지:

1. 중앙 집중식 중재:
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐           │
│  │Device 0│  │Device 1│  │Device 2│  │Device 3│           │
│  └───┬────┘  └───┬────┘  └───┬────┘  └───┬────┘           │
│      │  REQ      │  REQ      │  REQ      │  REQ            │
│      │           │           │           │                  │
│      └─────┬─────┴─────┬─────┴─────┬─────┘                  │
│            │           │           │                        │
│            ▼           ▼           ▼                        │
│  ┌──────────────────────────────────────────────┐          │
│  │              Bus Arbiter                      │          │
│  │           (중앙 중재기)                        │          │
│  │  - 요청 수신                                  │          │
│  │  - 우선순위에 따라 GRANT 신호 발행            │          │
│  └──────────────────────────────────────────────┘          │
│            │           │           │                        │
│      GRANT │     GRANT │     GRANT │                        │
│            ▼           ▼           ▼                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2. 우선순위 방식:
- 고정 우선순위: 장치마다 고정된 우선순위
- 라운드 로빈: 순환하며 공평하게 할당
- 동적 우선순위: 사용 패턴에 따라 조정

6. I/O 인터페이스

6.1 I/O 주소 지정

1. 분리 I/O (Isolated I/O / Port-Mapped I/O):
┌─────────────────────────────────────────────────────────────┐
                                                             
  메모리 주소 공간                I/O 주소 공간              
  ┌────────────────────┐        ┌────────────────────┐      
   0x0000_0000                 0x0000                   
                                                        
        Memory                    I/O Ports             
                                                        
   0xFFFF_FFFF                 0xFFFF                   
  └────────────────────┘        └────────────────────┘      
                                                             
  - 별도의 주소 공간 사용                                     
  - IN, OUT 명령어 사용                                      
  - x86 아키텍처에서 사용                                    
                                                             
  예시:                                                      
  outb(0x3F8, data);   // 포트 0x3F8에 쓰기                  
  data = inb(0x3F8);   // 포트 0x3F8에서 읽기                
                                                             
└─────────────────────────────────────────────────────────────┘

2. 메모리  I/O (Memory-Mapped I/O):
┌─────────────────────────────────────────────────────────────┐
                                                             
  통합 주소 공간                                             
  ┌────────────────────────────────────────────┐            
   0x0000_0000                                            
                                                          
        System Memory (RAM)                               
                                                          
   0x7FFF_FFFF                                            
  ├────────────────────────────────────────────┤            
   0x8000_0000                                            
                                                          
        I/O Device Registers                              
        (메모리처럼 접근)                                   
                                                          
   0xFFFF_FFFF                                            
  └────────────────────────────────────────────┘            
                                                             
  - 일반 메모리 명령어로 I/O 접근                             
  - ARM, RISC-V 등에서 주로 사용                             
  - 현대 PC에서도 대부분의 장치가 MMIO 사용                   
                                                             
  예시:                                                      
  volatile uint32_t* reg = (uint32_t*)0xFE200000;           
  *reg = value;         // I/O 레지스터에 쓰기               
  value = *reg;         // I/O 레지스터에서 읽기             
                                                             
└─────────────────────────────────────────────────────────────┘

6.2 장치 드라이버 구조

┌─────────────────────────────────────────────────────────────┐
                    디바이스 드라이버 구조                     
├─────────────────────────────────────────────────────────────┤
                                                             
  ┌─────────────────────────────────────────────────────┐   
                Driver Entry Points                        
  ├─────────────────────────────────────────────────────┤   
    init()      - 드라이버 초기화                          
    open()      - 장치 열기                                
    close()     - 장치 닫기                                
    read()      - 데이터 읽기                              
    write()     - 데이터 쓰기                              
    ioctl()     - 제어 명령                                
    interrupt_handler() - 인터럽트 처리                    
  └─────────────────────────────────────────────────────┘   
                                                             
  ┌─────────────────────────────────────────────────────┐   
                Driver Internal Data                       
  ├─────────────────────────────────────────────────────┤   
    - 장치 상태                                            
    - 버퍼                                                 
    - 대기                                               
    - 설정 정보                                            
  └─────────────────────────────────────────────────────┘   
                                                             
└─────────────────────────────────────────────────────────────┘

리눅스 디바이스 드라이버 예시:
static struct file_operations my_fops = {
    .owner   = THIS_MODULE,
    .open    = my_open,
    .release = my_close,
    .read    = my_read,
    .write   = my_write,
    .unlocked_ioctl = my_ioctl,
};

7. 현대 I/O 시스템

7.1 NVMe (Non-Volatile Memory Express)

┌─────────────────────────────────────────────────────────────┐
│                    NVMe 아키텍처                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  기존 SATA/AHCI vs NVMe:                                    │
│                                                             │
│  SATA/AHCI:                                                 │
│  - 단일 명령 큐 (깊이 32)                                    │
│  - HDD 시대에 설계                                          │
│  - 높은 지연시간                                            │
│                                                             │
│  NVMe:                                                      │
│  - 64K 큐, 각 큐당 64K 명령                                 │
│  - SSD에 최적화                                             │
│  - 낮은 지연시간                                            │
│  - PCIe 직접 연결                                           │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                   CPU / Driver                       │   │
│  └─────────────────────────┬───────────────────────────┘   │
│                            │                                │
│           ┌────────────────┼────────────────┐              │
│           │                │                │              │
│           ▼                ▼                ▼              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │ Submit Q 0  │  │ Submit Q 1  │  │ Submit Q N  │        │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘        │
│         │                │                │                │
│         └────────────────┼────────────────┘                │
│                          │                                  │
│                          ▼                                  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                 NVMe Controller                       │  │
│  └──────────────────────────────────────────────────────┘  │
│                          │                                  │
│         ┌────────────────┼────────────────┐                │
│         │                │                │                │
│         ▼                ▼                ▼                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │Complete Q 0 │  │Complete Q 1 │  │Complete Q N │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.2 USB 시스템

┌─────────────────────────────────────────────────────────────┐
│                    USB 아키텍처                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  USB 세대별 속도:                                           │
│  ┌──────────────┬──────────────────────────────────────┐   │
│  │ USB 2.0     │  480 Mbps (High Speed)               │   │
│  │ USB 3.0     │  5 Gbps (SuperSpeed)                 │   │
│  │ USB 3.1     │  10 Gbps (SuperSpeed+)               │   │
│  │ USB 3.2     │  20 Gbps (SuperSpeed USB 20Gbps)     │   │
│  │ USB4        │  40 Gbps                              │   │
│  └──────────────┴──────────────────────────────────────┘   │
│                                                             │
│  USB 전송 타입:                                             │
│  ┌──────────────┬──────────────────────────────────────┐   │
│  │ Control     │ 설정, 제어 (작은 데이터)              │   │
│  │ Bulk        │ 대용량 데이터 (저장 장치)             │   │
│  │ Interrupt   │ 소량, 주기적 (키보드, 마우스)         │   │
│  │ Isochronous │ 실시간, 주기적 (오디오, 비디오)       │   │
│  └──────────────┴──────────────────────────────────────┘   │
│                                                             │
│  USB 토폴로지:                                              │
│                                                             │
│       Host Controller                                       │
│            │                                                │
│            ▼                                                │
│        Root Hub                                             │
│        /   |   \                                            │
│       /    |    \                                           │
│    Hub  Device  Device                                      │
│    / \                                                      │
│   /   \                                                     │
│ Dev   Dev                                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.3 가상화 I/O

┌─────────────────────────────────────────────────────────────┐
│                   I/O 가상화 기법                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 에뮬레이션:                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Guest OS  →  Hypervisor  →  Physical Device        │   │
│  │            (I/O 트랩 및 에뮬레이션)                   │   │
│  └─────────────────────────────────────────────────────┘   │
│  - 느림, 호환성 높음                                        │
│                                                             │
│  2. Para-virtualization (virtio):                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Guest OS (virtio 드라이버) → Hypervisor → Device   │   │
│  └─────────────────────────────────────────────────────┘   │
│  - 최적화된 가상 인터페이스                                 │
│  - 게스트 OS 수정 필요                                     │
│                                                             │
│  3. Direct Device Assignment (VFIO):                       │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Guest OS  →  Physical Device (직접 접근)           │   │
│  │            (IOMMU로 메모리 격리)                     │   │
│  └─────────────────────────────────────────────────────┘   │
│  - 네이티브 성능                                           │
│  - 장치 공유 불가                                          │
│                                                             │
│  4. SR-IOV (Single Root I/O Virtualization):               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  하나의 물리 장치를 여러 가상 함수(VF)로 분할         │   │
│  │  각 VM이 독립적인 VF에 직접 접근                      │   │
│  └─────────────────────────────────────────────────────┘   │
│  - 네이티브 성능 + 공유 가능                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

8. 연습 문제

기초 문제

  1. 폴링, 인터럽트, DMA 방식의 차이점을 설명하시오.

  2. 인터럽트 벡터 테이블의 역할은?

  3. DMA 컨트롤러에 설정해야 하는 정보 3가지는?

중급 문제

  1. 다음 상황에서 적합한 I/O 방식을 선택하시오:
  2. (a) 키보드 입력 처리
  3. (b) 10MB 파일 디스크에서 읽기
  4. (c) 고속 네트워크 패킷 처리 (10Gbps)

  5. 인터럽트 기반 I/O에서 발생할 수 있는 "Interrupt Storm"이란?

  6. 메모리 맵 I/O와 포트 맵 I/O의 장단점을 비교하시오.

심화 문제

  1. 다음 조건에서 CPU 효율을 계산하시오:
  2. CPU 클럭: 3GHz
  3. 디스크 전송률: 500MB/s
  4. DMA 블록 크기: 4KB
  5. DMA 완료 인터럽트 처리 시간: 1000 사이클

  6. USB의 4가지 전송 타입을 설명하고 각각에 적합한 장치 예를 들시오.

  7. SR-IOV가 가상화 환경에서 I/O 성능을 향상시키는 원리를 설명하시오.

정답 1. I/O 방식 비교: - 폴링: CPU가 계속 상태 확인, 간단하지만 CPU 낭비 - 인터럽트: 장치가 완료 알림, CPU 효율적이지만 오버헤드 - DMA: 메모리 직접 전송, 대용량에 효율적이지만 복잡 2. 인터럽트 벡터 테이블: - 인터럽트 번호를 핸들러 주소로 매핑 - 인터럽트 발생 시 해당 핸들러로 점프 3. DMA 설정 정보: - 소스/목적지 주소 - 전송 크기 (바이트 수) - 전송 방향 (읽기/쓰기) 4. 적합한 I/O 방식: - (a) 인터럽트 (저속, 비동기) - (b) DMA (대용량 블록 전송) - (c) DMA + 폴링 또는 NAPI (고속, 많은 패킷) 5. Interrupt Storm: - 너무 많은 인터럽트가 발생하여 CPU가 핸들러 처리만 하는 상황 - 정상 작업 수행 불가 - 해결: 인터럽트 병합, 폴링 전환 (NAPI) 6. I/O 주소 지정 비교: - 포트 맵: 별도 주소 공간, 별도 명령어 필요, 주소 공간 절약 - 메모리 맵: 통합 주소 공간, 일반 명령어 사용, 캐시 주의 필요 7. CPU 효율 계산: - 전송률 500MB/s, 블록 4KB → 초당 125,000 전송 - 각 전송마다 1000 사이클 인터럽트 처리 - 총 인터럽트 사이클: 125,000,000 - CPU 효율: 1 - (125M / 3G) = 1 - 0.042 = 95.8% 8. USB 전송 타입: - Control: 장치 설정, 상태 확인 (모든 USB 장치) - Bulk: 대용량 데이터 (USB 드라이브, 프린터) - Interrupt: 소량 주기적 (키보드, 마우스) - Isochronous: 실시간 (웹캠, USB 오디오) 9. SR-IOV 원리: - 하나의 물리 장치에 여러 가상 함수(VF) 생성 - 각 VM이 전용 VF에 직접 접근 - 하이퍼바이저 개입 없이 네이티브 성능 - IOMMU로 메모리 격리 보장

다음 단계


참고 자료

  • Computer Organization and Design (Patterson & Hennessy)
  • Operating System Concepts (Silberschatz et al.)
  • NVMe Specification
  • USB Specification
  • Linux Device Drivers (Corbet, Rubini, Kroah-Hartman)
to navigate between lessons