12. I/O Systems

컴퓨터의 주된 두 가지 작업은 입출력과 계산이다. 이 때 운영 체제의 역할은 입출력 동작과 입출력 장치를 관리하는 것이다. 이를 알아보자.

12.1. Overview

컴퓨터에 연결된 장치의 제어는 운영 체제에서 중요한 고려 요소이다. 입출력 장치는 기능에서부터 속도까지 매우 다양해서 이를 제어하는 방법도 다양하기 때문이다. 운영 체제는 입출력 기기 제어에 대한 세부 사항과 서로 다른 장치간의 차이점을 캡슐화하기 위해 장치 드라이버를 사용한다.

12.2. I/O Hardware

컴퓨터는 다양한 장치를 운용하나 우리는 장치가 어떻게 부착되고 소프트웨어가 어떻게 하드웨어를 제어하는지만 알면 된다. 장치는 기기와 연결 부위(포트)를 통해 통신한다. 이는 물리층(PHY) 이라고도 한다. 버스는 와이어의 집합과 엄격하게 정의된 프로토콜로 와이어에 정해질 수 있는 메시지의 집합을 특정한다. 데이지 체인은 대개 버스로 기능한다. 컴퓨터에서는 대개 PCIe 버스가 쓰이며 이는 프로세서-메모리 서브시스템을 빠른 장치와 연결한다. 확장 버스는 상대적으로 느린 장치와 연결된다. PCIe 버스에서 디스크는 직렬 연결 SCSI(SAS)를 통해 연결되곤 한다.

제어 장치는 포트, 버스, 장치를 제어할 수 있는 전자 기기의 모임이다. 시리얼 포트 제어 장치는 단순하다. 파이버 채널(FC) 버스 제어 장치는 단순하지 않기 때문에 FC 버스 제어 장치는 대개 별개의 회로 판 (호스트 버스 어댑터(HBA))로 구현된다.

12.2.1. Memory-Mapped I/O

프로세서는 어떻게 제어 장치에 명령을 하고 데이터를 전송할까? 제어 장치에 데이터와 제어 신호를 위한 레지스터가 있기 때문이다. 프로세서와 제어 장치간 통신을 하는 하나의 방법은 특수한 입출력 명령어를 사용하는 방법도 있고, 장치가 메모리 맵 입출력을 지원해 레지스터가 프로세서의 주소 공간에 매핑되도록 하는 방법도 있다. 입출력 장치 제어는 대개 4개의 레지스터로 구성된다: 데이터-인 레지스터, 데이터-아웃 레지스터, 상태 레지스터, 제어 레지스터.

12.2.2. Polling

호스트와 포트간 통신에서 최초에 호스트는 바쁨 대기 또는 폴링 상태로, 상태 레지스터의 busy 비트가 클리어되기까지 기다린다. 이후 command 레지스터의 write 비트를 세팅하고 data-out 레지스터에 바이트를 쓴다. 호스트는 command-ready 비트를 세팅한다. 제어 장치가 이를 감지하면 busy 비트를 세팅한다. 제어 장치는 command 레지스터를 읽고 write 커맨드를 본 뒤 data-out 레지스터의 데이터를 읽고 입출력을 수행한다. 이후 command-ready 비트, error 비트, busy 비트를 청소한다. 그러나 폴링으로 상태 레지스터를 계속 대기하는 대신 상태 레지스터가 준비되면 CPU에 인터럽트로 알려 주는 방식을 취할 수도 있다.

12.2.3. Interrupts

기본 인터럽트 메커니즘은 다음과 같다. CPU 하드웨어는 인터럽트 요청 라인을 갖고 있어서 임의의 명령어를 실행할 때마다 이를 감지할 수 있다. 제어 장치가 인터럽트 신호를 보내면 CPU는 상태를 저장하고 인터럽트 핸들러 루틴으로 점프해 이를 처리하고 인터럽트로부터 반환된다. 현대 운영 체제에서는 조금 더 복잡한데, 임계 프로세싱 중 인터럽트 핸들링을 미룰 수 있어야 하며, 모든 장치를 폴링시키는 대신 장치들에 적절한 인터럽트 핸들러를 보낼 수 있는 효율적인 방법이 필요하고, 인터럽트간 우선 순위도 두어야 하며, 운영 체제의 이목을 직접적으로 끌 수 있는 명령어도 필요하다. 현대 컴퓨터 하드웨어에서는 이런 특성들은 CPU와 인터럽트 제어 하드웨어로부터 제공된다.

대부분의 CPU는 2개의 인터럽트 요청 라인을 갖고 있다. 하나는 마스킹 불가능 인터럽트이며 회복 불가능한 메모리 에러 등을 위해 예약되어 있다. 하나는 마스킹 가능 인터럽트이며 이것이 장치 제어 기기에 의해 서비스 요청을 위해 사용된다. 인터럽트 메커니즘은 주소를 받으며 대부분의 아키텍쳐에서 이는 인터럽트 벡터라는 테이블 내에서의 위치이다. 이의 목적은 단일 인터럽트가 모든 가능한 인터럽트 지점을 탐색해야 하는 일을 방지하기 위한 것이다. 일반적으로는 인터럽트 연쇄라는 방법을 쓰는데 인터럽트 벡터의 모든 원소가 인터럽트 핸들러의 목록의 제일 앞부분을 가리키도록 한다.

인터럽트 메커니즘은 인터럽트 우선도 단계도 구현한다. 또한, 인터럽트 메커니즘은 많은 예외를 처리하기도 한다. 인터럽트 제어는 시간과 자원을 많이 소모하고 구현하기 복잡하기 때문에, 시스템들은 대개 인터럽트 관리를 1단계 인터럽트 핸들러(FLIH)와 2단계 인터럽트 핸들러(SLIH)로 분리한다. 운영 체제는 인터럽트를 매우 잘 활용하는데 가상 메모리 페이징의 페이지 폴트에 쓰기도 하고 시스템 호출에서 소프트웨어 인터럽트(트랩)을 생성하기도 한다. 인터럽트는 커널의 제어 흐름을 조정하는 데도 쓰인다. 요약하자면, 인터럽트는 현대 운영 ㅈ체제에서 비동기 이벤트를 제어하고 트랩을 통해 커널을 관리자 모드 루틴으로 보낸다.

12.2.4. Direct Memory Access

디스크 드라이브같이 큰 전송을 수행하는 기기에 대해서는 범용 프로세서를 사용해 제어 장치 레지스터에 한 번에 한 바이트씩 프로그램 입출력 (PIO)을 수행하는 것은 낭비스럽다. 그래서 컴퓨터는 이 작업의 일부를 직접 메모리 접근(DMA) 컨트롤러라는 특수 목적 프로세서에 이를 오프로딩시킨다. 이 때 호스트는 여러 곳에 흩어져 있는 출발지와 도착지 주소를 관리하는 산포-수집 방법을 쓴다. 이것은 커널 공간의 이야기이고 사용자 공간에서는 전송 중에 내용물이 수정 중일 수 있으므로 이중 버퍼링이 발생할 수 있다. 이는 비효율적이므로 운영 체제들은 시간에 걸쳐 메모리 매핑을 활용하기 시자하였다.

DMA 제어 장치와 장치 제어 장치간 핸드셰이킹은 DMA-요청, DMA-인정이라는 와이어 쌍을 통해 이루어진다. 전체 전송이 끝나면 DMA 제어 장치는 CPU를 인터럽트한다. 이 사이클 훔침은 CPU 연산을 느리게 할 수 있지만, 데이터 전송을 DMA 컨트롤러에 오프로딩하는 것은 전체적인 관점에서는 성능을 증가시킨다. 어떤 컴퓨터는 DMA에 물리적 메모리를 쓰기도 하지만 어떤 컴퓨터는 직접 가상 메모리 접근(DVMA)를 쓴다. 보호 모드인 커널에서는 운영체제는 프로세스가 장치 명령어를 직접적으로 실행하지 못하게 하고 권한을 부여받은 간접적인 함수들을 통해 이를 수행한다.

12.2.5 I/O Hardware Summary

입출력 하드웨어들의 주된 개념들은 다음과 같다: 버스, 컨트롤러, 입출력 포트와 레지스터, 호스트와 장치 제어장치간 핸드셰이킹, 폴링 순환 또는 인터럽트를 통한 핸드셰이킹의 실행, 큰 전송에 대해 DMA 제어 장치로의 작업 오프로딩.

12.3. Application I/O Interface

애플리케이션은 어떻게 디스크의 종류와 디스크/장치가 컴퓨터에 부착되는 방식을 알 필요 없 이 입출력을 수행하는가? 이는 추상화된 인터페이스를 통해 이루어진다. 이런 장치 드라이버 층의 목적은 커널의 입출력 부분시스템으로부터 장치 제어 장치간 차이점을 숨기는 것이다. 이는 입출력 시스템 호출이 장치의 동작을 캡슐화해서 애플리케이션으로부터 하드웨어간 차이를 숨기는 것과 같다. 장치 하드웨어 생산자들에겐 안된 일이지만, 장치 드라이버 인터페이스는 운영 체제마다 제각각이다. 장치는 여러 방면에서 다를 수 있다: 문자 스트림/블록, 순차적/무작위 접근, 동기/비동기, 공유 가능/불가능, 연산 속도, 읽기 전용/읽기-쓰기/1회용 쓰기. 이런 부분들은 운영 체제로부터 숨겨져 있다. 대부분의 운영 체제는 탈출구(백도어)가 존재하여 애플리케이션에서 장치 드라이버로 임의의 명령어를 직접 전달시킬 수 있다.

12.3.1. Block and Character Devices

블록-장치 인터페이스는 디스크 드라이브와 다른 블록 중심 장치들에 접근하는 데 필요한 모든 부분을 전담한다. 운영 체제는 그 자체로, 데이터베이스 관리 시스템같은 특별 애플리케이션과 비슷하게, 블록 장치를 블록의 단순한 선형 배열처럼 보고 접근할 수 있다. 이를 원시 입출력 모드라 한다. 자체 버퍼링과 락이 있는 애플리케이션에 대처하기 위해, 운영 체제는 버퍼링과 락을 비활성화하는 모드로 파일에 대한 연산을 할 수가 있다. 이를 직접 입출력이라 한다. 메모리 매핑된 파일 접근은 블록 장치 드라이버의 최상층에 위치할 수 있다. 키보드는 문자열 스트림 인터페이스로 접근되는 장치의 예이다.

12.3.2. Network Devices

네트워크 입출력은 디스크 입출력과 특성과 성능이 다르기 때문에, 소켓 인터페이스를 쓴다.

12.3.3. Clocks and Timers

많은 컴퓨터들은 타이머와 시계를 통해 현재 시간, 지난 시간을 계산하고 타이머를 세팅할 수 있다. 이를 게산하는 하드웨어는 프로그래밍 가능한 구간 타이머라 불린다. 많은 컴퓨터들은 10MHz로 동작하는 고성능 이벤트 타이머(HPET)를 갖고 있다. 타이머로 발생하는 오차를 보정하기 위해 컴퓨터는 네트워크 시간 프로토콜(NTP)를 가진다.

12.3.4. Nonblocking and Asynchronous I/O

애플리케이션이 블로킹 시스템 호출을 할 때에는 호출 스레드의 실행이 중단된다. 이에 반해 어떤 사용자 레벨 프로세스는 논블로킹 입출력을 수행하기도 한다. 아니면 이에 대한 대안으로 비동기 시스템 호출을 할 수도 있다. 비동기 호출은 입출력 대기가 완료되기 전에 즉시 반환되고, 스레드는 코드 실행을 계속한다. 이러한 비동기 동작은 현대 운영 체제에서 폭넓게 쓰인다.

12.3.5. Vectored I/O

벡터화된 입출력은 한 시스템 호출이 복수의 위치에 대한 복수의 입출력 동작을 수행하게 한다. 이러한 산포-수집 방법은 여러 이유로 유용하다.

12.4. Kernel I/O Subsystem

커널의 입출력 부분시스템을 알아보자.

12.4.1. I/O Scheduling

입출력 요청을 스케쥴링한다는 것은 그를 실행할 순서를 정하는 것을 말한다. 운영 체제 개발자들은 각 장치에 대해해 요청의 대기 큐를 관리해 이를 구현한다. 커널이 비동기 입출력을 지원할 때에는 동시에 일어나는 입출력 요청을 추적하기 위해 장치 상태 테이블에 이를 관리한다.

12.4.2. Buffering

버퍼는 두 장치간 또는 장치-애플리케이션간 전송되는 데이터를 저장하는 메모리 지역이다. 장치에 대한 쓰기는 즉각적이지 않고 네트워크 인터페이스는 장치에 쓰는 동안 받는 데이터를 저장할 곳이 필요하기 때문에 이중 버퍼링을 사용해서 데이터의 제공자와 요청자를 분리하고 그들간 시간 요청 간격을 완화한다. 또한 이는 데이터 전송 크기가 다른 기기들간 적응을 하는 데 쓰이기도 한다. 또한, 애플리케이션 입출력에 복사 시맨틱을 적용함으로써 디스크에 써지는 데이터의 버전이 애플리케이션 시스템 호출시의 버전과 동일함을 보장하는 용도도 있다.

12.4.3. Caching

캐시는 데이터의 복제를 저장하는 빠른 메모리 지역이다. 이는 자주 입출력되는 데이터를 저장해 효율을 높인다.

12.4.4. Spooling and Device Reservation

스풀은 인터리빙된 데이터 스트림을 받을 수 없는 프린터 같은 장치에 대해 출력을 수행하는 버퍼이다. 여러 애플리케이션이 프린터에 쓰기 요청을 동시에 할 수 있지만 프린터는 한 번에 하나의 작업만 수행할 수 있기 때문에 스풀이 쓰인다.

12.4.5. Error Handling

에러를 다루기 위해 입출력 시스템 호출은 호출 상태에 대해 성공/실패를 알리는 단일 비트를 반환한다. 예를 들어 SCSI 프로토콜이 호출한 SCSI 기기의 실패는 3단계로 세분화된다: 실패의 일반적인 특성을 특정하는 분별 키, 실패의 카테고리를 특정하는 추가 분별 코드, 추가 세부사항을 담는 추가 분별 코드 식별자.

12.4.6. I/O Protection

사용자가 잘못된 입출력을 수행하는 것을 막기 위해 모든 입출력 명령어는 권한이 필요한 명령어로 둔다.

12.4.7. Kernel Data Structures

커널은 입출력 컴포넌트의 상태를 관리하기 위한 자료 구조를 둔다. 그 세부 사항은 운영 체제마다 다르다.

12.4.8. Power Management

컴퓨터의 전력 소모를 관리하는 것은 운영 체제이다. 이는 기본적으로 장치 관리에 기반한다. 현대적인 범용 컴퓨터는 고급 구성과 전원 인터페이스(ACPI)라는 별개의 펌웨어 코드 집합을 둬서 이러한 하드웨어의 특성을 관리한다.

12.4.9. Kernel I/O Subsystem Summary

입출력 시스템은 다음을 수행한다: 파일과 장치의 이름공간 관리, 파일과 장치에 대한 접근 제어, 동작 제어, 파일 시스템 공간 할당, 장치 할당, 버퍼링/캐싱/스풀링, 입출력 스케쥴링, 장시 상태 모니터링, 오류 처리, 실패에 대한 복구, 장치 드라이버 설정 및 초기화, 입출력 장치의 전력 관리.

12.5. Transforming I/O Requests to Hardware Operations

입출력 요청을 하드웨어 동작으로 어떻게 변환할까? UNIX에서는 특정 장치를 파일 경로와 연결짓는 마운트 테이블로 이를 관리한다. 입출력 동작의 CPU 사이클은 다음과 같다:

  1. 프로세스는 블로킹 read() 시스템 호출을 이전에 열린 파일의 파일 디스크립터에 수행한다.
  2. 커널의 시스템 호출 코드는 매개 변수의 정당성을 검증한다.
  3. 데이터가 버퍼 캐시에 없다면 물리적 입출력을 수행한다.
  4. 장치 드라이버는 커널 버퍼 공간을 할당해 데이터를 받고 입출력을 스케쥴ㄹ이한다.
  5. 장치 컨트롤러는 장치 하드웨어를 조종해 데이터 전송을 수행한다.
  6. 드라이버는 상태나 데이터를 폴링하거나 DMA 전송을 커널 메모리에 배치한다.
  7. 인터럽트 핸들러는 인터럽트 벡터 테이블을 통해 인터럽트를 받고, 필요한 데이터를 저장하고, 장치 드라이버를 호출하고, 인터럽트로부터 반환한다.
  8. 장치 드라이버는 신호를 받고 어떤 입출력 요청이 완료되었는지 판별하고 요청의 상태를 판별하고 요청이 완료되면 커널 입출력 시스에 신호를 보낸다.
  9. 커널은 데이터나 반환 코드를 요청 프로세스의 주소 공간에 전송하고 프로세스를 대기 큐로부터 준비 큐로 이동시킨다.
  10. 프로세스가 준비 큐로 이동되면 언블로킹되어 실행이 재개된다.

12.6. STREAMS

UNIX 시스템은 스트림이라는 메커니즘으로 드라이버 파이프라인의 코드를 동적으로 집합시킨다. 이는 사용자 프로세스와의 인터페이스를 맡는 스트림 헤드, 장치를 제어하는 드라이버 엔드, 그리고 그 사이에 몇 개의 스트림 모듈로 구성된다. 모듈의 큐간 메시지 교환을 통해 한 큐가 다른 큐를 넘치게 하는 일을 막기 위해 큐는 흐름 제어를 지원한다.

12.7. Performance

입출력은 시스템 성능의 핵심 요소이다. 어떤 시스템은 별개의 프론트엔드 프로세서를 둬서 메인 CPU에 걸리는 인터럽트 부하를 줄인다. 예를 들어, 터미널 집중기는 수백 개의 리모트 터미널의 트래픽을 큰 컴퓨터의 단일 포트로 집중시킨다. 이러한 입출력 채널은 메인프레임이나 다른 하이엔드 시스템에서 찾아볼 수 있는 특수 목적 CPU이다. 입출력 효율을 높이는 원리는 다음이 있다.

  • 컨텍스트 전환의 수를 줄인다.
  • 장치와 애플리케이션간 데이터 전송 시 반드시 일어나야 하는 데이터 복사 횟수를 줄인다.
  • 큰 전송, 스마트 컨트롤러, 폴링을 사용해 인터럽트 빈도를 줄인다.
  • DMA를 인지하는 컨트롤러나 채널을 사용해 단순한 데이터 복제를 오프로딩해 동시성을 증가시킨다.
  • 원시 프로세싱을 하드웨어로 전담시켜 CPU/버스 연산과 동시에 일어나게 한다.
  • CPU, 메모리 서브시스템, 버스, 입출력 성능간 부하를 균형을 맞춘다.

입출력 기능은 어디에 구현되어야 할까?

  • 최초의 입출력 알고리즘은 애플리케이션 레벨에 둔다. 가장 유연하고 버그도 덜 치명적이기 때문이다.
  • 애플리케이션 레벨 알고리즘이 충분히 검증되면 커널에 재구현한다.
  • 최고의 성능은 하드웨어에 특화된 구현으로부터 나온다. 구현이 유연하지 못하고 버그 수정이 어려워진다는 점이 있다.

12.8. Summary

  • 입출력에 포함되는 기본적인 하드웨어 요소는 버스, 장치 컨트롤러, 장치 그 자체이다.
  • 장치와 메인 메모리간 데이터 이동은 CPU에 의해 프로그램된 입출력을 통해 또는 DMA 컨트롤러에 오프로딩시켜서 수행된다.
  • 장치를 제어하는 커널 모듈은 장치 드라이버이다. 애플리케이션에 제공되는 시스템 호출 인터페이스는 여러 기본적 하드웨어 카테고리를 다루도록 설계되어 있다. 블록 장치, 문자-스트림 장치, 메모리 매핑된 파일, 네트워크 소켓, 프로그램된 구간 타이머. 이러한 시스템 호출은 이를 일으킨 프로세스를 정지시키지만, 애플리케이션이 입출력 동작이 완료되는 동안 정지하면 안 되는 경우라면 커널 자신에 의해 비정지 또는 비동기 호출이 사용되기도 한다.
  • 커널의 입출력 부분시스템은 여러 서비스를 제공한다. 이들엔 입출력 스케쥴링, 버퍼링, 캐싱, 스풀링, 장치 예약, 오류 대처. 다른 서비스인 이름 변환은 하드웨어 장치와 애플리케이션에 의해 사용되는 상징적 파일명을 연결시킨다. 이는 문자열로부터 특정 장치 드라이버나 장치 주소로, 그리고 입출력 포트나 버스 컨트롤러의 물리적 주소로 변환되는 여러 층의 매핑을 포함한다. 이러한 매핑은 UNIX처럼 파일 시스템 이름공간 내부에서 발생할 수도 있고 MS-DOS처럼 별개의 장치 이름공간에서 발생할 수도 있다.
  • STREAMS는 장치 드라이버와 네트워크 프로토콜에 쓰기를 수행하는 모듈화되고 점진적인 접근법에 대한 프레임워크를 제공하는 구현과 방법론이다. STREAMS를 통해 드라이버들은 쌓여서 데이터가 그들간 순차적으로 그리고 양방향으로 이동하게 할 수 있다.
  • 입출력 시스템 호출은 물리적 기기와 애플리케이션간 소프트웨어 층 여러 개를 동반하기 때문에 CPU 소모에 있어 비용이 크다. 이런 층은 여러 오버헤드를 함축한다: 커널의 보호 경계를 넘나드는 컨텍스트 전환, 입출력 장치를 서비스하기 위한 시그널과 인터럽트 핸들링, 커널 버퍼와 애플리케이션 공간 사이의 데이터를 복제하기 위한 CPU와 메모리 시스템에 대한 부하.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중