logo
Published on

I/O

Authors
  • avatar
    Name
    valery
    Twitter

1. I/O Hardware(물리적으로 어떻게 구성되어있는지)

device controller, device register

1. Bus 구조

2. Device Controller (host adapter)

1. 장치의 두 구성요소

  • Mechanical component (기계부)
  • Electronic component (= controller 들어있음)

2. Controller가 하는일

  • 직렬 비트 스트림 → 바이트 블록 변환
  • 메인 메모리에 전달
  • 필요시 에러 정정
  • 여러 장치 동시 핸들 가능

3. Device Registers

  • 종류: data-in / data-out / status / control (1~4 byte 또는 FIFO)
  • 보통 1~4 byte, 또는 FIFO 버퍼

2. 레지스터에 읽거나 쓰는 방법 2가지 ← ※ #2.2와 동일 개념(중복)

  • Direct I/O / Memory-mapped

3. 예시: Simple Printer

  • status register: done bit(출력 완료) / error bit(종이 걸림·종이 없음)
  • data register: 출력할 데이터
  • CPU 동작: done bit 셋 될 때까지 대기 + 반드시 error bit 확인

2. I/O Device(동작을 5가지 관점으로 분류)

1. 데이터를 무슨 단위로 주고 받냐?

1. Block(고정 블록)

  • 정의: 고정 크기 블록 단위로 정보 저장, 각 블록마다 고유 주소가 있음
  • 특징: 블록 크기 512B~32KB / 블록 단위로 독립 read·write / 고유 주소가 있어서 → seek 가능 (랜덤 접근(순차 접근과 반대의 개념, 랜덤으로 접근한다는 말이 아니라 건너 뛰어서 원하는 위치를 딱 찾을 수 있다.))
  • 예시: 디스크, 테이프

2. Character(문자 스트림)

  • 정의: 문자 스트림을 한 글자씩 주고받음
  • 특징: 주소 없음(not addressable) / seek 불가 (순차적)
  • 예시: 프린터, 모뎀, 마우스, 키보드

2. CPU가 장치 레지스터에 어떻게 접근하냐?

1. Direct I/O(전용 명령어)

CPU 입장에서 주소공간이 두 개 있다 — 하나는 메모리 주소공간(RAM 읽고 쓰는 곳), 또 하나는 I/O 포트 주소공간(장치 레지스터 있는 곳).

이 둘이 완전히 분리돼 있어서, 메모리 접근하는 명령어로는 장치에 못 가고 in/out 같은 전용 명령어를 따로 써야한다

2. Memory-mapped(메모리 주소)

  • 정의: 장치 제어 레지스터를 프로세서의 메모리 주소공간 안에 매핑 → 레지스터를 그냥 메모리 주소처럼 취급
  • 특징: 별도 I/O 명령어 없이 표준 데이터 전송 명령어(load/store)로 접근
  • 장점:
    • 드라이버를 순수 C로 작성 가능 (어셈블리 in/out 불필요)
    • 별도 보호 메커니즘 불필요 → 기존 페이지 테이블(PTE)로 보호, 원하는 페이지를 페이지 테이블에 넣어 특정 사용자에게 특정 장치 제어권까지 줄 수 있음
    • 레지스터 읽고 값 테스트를 단일 명령어로 처리

3. CPU가 I/O 처리됐는지 어떻게 아나?

1. Polling(계속 확인)

  • 정의: CPU가 장치한테 "관심 필요해?"를 직접 반복해서 물어봄(poll)
    • 명령 받을 준비됐는지 / 명령 상태가 어떤지 등을 확인
  • 장점:
    • 단순함 (status register 확인 루프만 돌면 됨)
    • 소프트웨어가 제어권을 쥠 (언제 확인할지 CPU가 결정)
    • 장치가 곧바로 준비되면 오히려 효율적
  • 단점:
    • 복잡한 시스템에선 비효율 → CPU 사용률만 높아짐 (busy-wait)
    • 낮은 우선순위 장치는 영영 서비스 못 받을 수도

2. Interrupt(신호 받기)

  • 정의: 장치가 관심 필요할 때 CPU한테 인터럽트를 요청 → CPU가 알려줄 때까지 기다림
  • 특징:
    • 장치별 전용 ISR(인터럽트 서비스 루틴)이 호출됨
    • 여러 장치가 인터럽트를 공유할 수 있음
  • 장점:
    • CPU는 진짜 필요할 때만 장치에 개입 (평소엔 다른 일 함)
    • 일반적으로 폴링보다 효율적
  • 단점:
    • 인터럽트가 과도하면 본 프로그램 실행이 느려지거나 막힘
    • 오버헤드 (전송 바이트당 인터럽트 1번 필요할 수도) ← 1.4 DMA 등장 이유
  • 처리 흐름(7단계):
    1. 드라이버가 I/O 시작
    2. 컨트롤러가 I/O 수행
    3. 완료/에러 시 인터럽트 신호 발생(wait queue -> ready queue로 갈 때 )
    4. CPU가 인터럽트 받고 핸들러로 제어 넘김
    5. 핸들러가 데이터 처리 후 복귀
    6. CPU가 원래 작업 재개 (CPU는 매 명령어 사이마다 인터럽트 들어왔나 확인)

4. 데이터를 실제로 누가 옮기나?

1. Programmed I/O(CPU가 옮기기)

  • 정의: CPU가 장치 ↔ 메모리 데이터 이동에 직접 관여 (한 워드씩 CPU 손을 거침)
  • 특징: 전용 I/O 명령어 또는 memory-mapped 방식으로 옮김
  • 단점: 대용량 전송 시 CPU가 바이트마다 매달려야 함 → CPU 낭비

2. DMA (컨트롤러가 옮기기)

  • 정의: DMA 컨트롤러가 CPU를 거치지 않고 장치 ↔ 메모리를 직접 전송
  • 특징:
    • 메모리 속도에 가깝게 쏟아내는 고속 장치용
    • 블록 단위로 버퍼 → 메인 메모리 직접 전송 (CPU 개입 없음)
    • 블록당 인터럽트 1번만 발생 ← 1.3.2 "바이트당 인터럽트" 단점 해결
  • 처리 흐름(4단계):
    1. CPU가 DMA 컨트롤러 세팅 (Address/Count/Control 레지스터)
    2. DMA가 메모리로 전송 요청 → 3. 데이터 직접 전송 → 4. 완료 시 Ack + 인터럽트

5. 호출한 프로세스를 어떻게 기다리나? (프로세스/시스템콜 레벨)

1. Blocking (대기)

  • 정의: I/O 끝날 때까지 프로세스가 suspend됨(잠듦)
  • 특징: 쓰기 쉽고 이해 쉬움 (코드가 위→아래 순서대로 흐름)
  • 예시: read(), write()
  • ※ 프로세스 상태도의 run→wait 전환 원인 = 바로 이 blocking I/O

2. Non-blocking (즉시 반환)

  • 정의: I/O 콜이 즉시 반환, 지금까지 전송된 바이트 수를 반환값으로 알려줌
  • 특징:
    • 데이터 없어도 안 기다리고 바로 다음 일 진행
    • 멀티스레딩으로 구현
    • select()로 데이터 준비됐는지 확인
  • 예시: select()

3. I/O Software(커널이 어떻게 추상화 하는지)

1. 목표

장치 독립성, 통일된 이름, 에러처리, 동기/비동기, 버퍼링, 공유/전용

2. Layer

1. User-level I/O Software

  • 대부분 라이브러리로 제공 (커널 밖, 유저 공간에서 돎)
    • C 표준 I/O 라이브러리: fopen(), fgets(), fscanf() 등 / 직접 만들어도 됨(myopen)
  • 핵심 구분 fopen() vs open():
    • open() = 시스템콜 (커널 직접 진입)
    • fopen() = open()을 감싼 라이브러리 함수 (버퍼링 등 추가)
  • 하는 일: I/O 호출 / 포맷팅(printf의 %d) / 스풀링(spooling)

2. Device-independent I/O Software

  • 역할: 모든 드라이버 위에 공통으로 얹히는 계층. 3.1 목표(통일된 이름·보호·버퍼링·할당·에러처리)가 실제로 구현되는 곳
  • (1) 통일된 인터페이스 (uniform interfacing):
    • 파일 보호 규칙이 장치에도 그대로 적용
  • (2) 에러 보고 (error reporting):
    • 많은 에러가 장치별 특수 → 해당 드라이버가 처리, 프로그래밍 에러 vs 실제 I/O 에러 구분
    • 처리 방법 5가지: 에러코드 반환 / 재시도 / 무시 / 호출 프로세스 kill / 시스템 종료

3. Device Drivers

  • 정의: 각 I/O 장치를 제어하는 장치별 전용 코드
  • 역할/위치:
    • 위로는 device-independent I/O 소프트웨어 + 인터럽트 핸들러와 소통, 아래로는 실제 하드웨어 제어
    • OS 나머지와 잘 정의된 모델·표준 인터페이스로 연결 → 장치 독립성(3.1)의 실제 구현 담당
  • 구현(로드) 방식 3가지:
    • 커널에 정적 링크 (박아넣음)
    • 부팅 시 선택적 로드
    • 실행 중 동적 로드 (핫플러그용 — USB 꽂으면 그때 로드)
  • 신뢰성 문제:
    • 드라이버가 OS 실패의 주원인 → Windows XP 크래시의 85%가 드라이버
    • Linux 커널 코드의 70%가 드라이버, 커널보다 7배 더 버그 많음
    • 이유: 경험 적은 개발자가 작성 + 종류 폭발 (XP에 35,000개 드라이버·120,000개 버전)

4. Interrupt Handlers

  • I/O 완료 시 드라이버를 깨움 (wake up driver)

5. Hardware

  • 실제 I/O 연산 수행