58_응용 입/출력 인터페이스 (Application I/O Interface)

 

입/출력 장치들이 일관된 방법으로 다루어 질 수 있도록 운영체제가 인터페이스를 구성하는 기술을 설명 한다. 공통적인 특징을 가진 입/출력 장치들의 표준 함수를 인터페이스(interface)라 한다. 장치 드라이버(device driver)라 불리는 커널 내의 모듈들은 각 입/출력 장치를 위한 구체적인 코딩을 제공하여 바로 위에서 정의한 "인터페이스"의 표준 함수들을 내부적으로 수행한다.

 

장치 드라이버(device driver) 계층의 목적은 여러 입/출력 하드웨어 차이를 숨기고 이들을 간단한 표준 인터페이스들로 보이도록 포장시켜서 이것을 상위의 커널 입/출력 서브시스템에 제공하는 것이다. 이렇게 되면 입/출력 서브시스템은 하드웨어와 독립적이 되어서 운영체제 개발자의 작업을 간단하게 해준다.

 

한 가지 문제점은 운영체제마다 장치 드라이버 인터페이스에 대한 규격이 다르다는 것이다. 따라서 새로운 장치를 위한 드라이버가 함께 제공되어야 한다.

 

  • 문자 스트림과 블록(Character stream or block) : 문자 스트림 장치는 바이트를 하나씩 전송하지만 블록 장치는 블록 단위로 전송 한다.
  • 순차 접근과 임의 접근(Sequential or random access) : 순차 장치는 순차적 순서로만 자료를 전송하지만 임의 접근 장치는 임의의 위치에 있는 자료도 입/출력 할 수 있다.
  • 동기식과 비동기식 (Synchronous or asynchronous) : 동기식 장치는 일정한 응답 시간을 갖지만 비동기식 장치는 응답 시간이 예측 불가능 하다.
  • 공유와 전용 (Sharable or dedicated) : 공유 가능한 장치는 몇 개의 프로세스나 쓰레드에 의해 동시에 사용될 수 있으나 전용 장치는 혼자만 사용해야 한다.
  • 읽기/쓰기, 읽기 전용, 쓰기 전용 : 일부 장치들은 읽기와 쓰기를 모두 수행하지만 어떤 것들은 하나만 지원한다.

 

[블록 장치와 문자 장치(Block and Character Device)]

블록 장치 인터페이스는 디스크나 이와 유사한 블록 지향(block oriented)장치를 사용하기 위해 필요한 모든 요소들을 제공하고 있다. 운영체제나 데이터베이스는 블록 장치를 마치 선형 배열(linear array)이라고 이해하고 사용하기를 원할 것이다. 이러한 접근 모드를 비가공 입/출력(raw I/O)라 불린다.

만약 응용프로그램이 자체 버퍼링을 수행 한다면 파일 시스템은 불필요하고 중복된 버퍼링을 하게 될 것이다. 또한 응용프로그램이 파일의 블록이나 일부에 대한 자체 잠금 기능을 제공한다면 운영체제의 잠금 기능은 최소한 중복된 기능이고 최악의 경우에는 모순이 발생 할 수도 있다. 이러한 충돌을 피하기 위해 비가공 장치(raw device)의 접근은 장치의 제어권을 집적 응용프로그램에 일임하고 운영체제는 한발 물러나야 한다. 이에 대한 절충안으로 운영체제가 버퍼링과 잠금을 하지 않는 모드로 파일에 대한 입/출력 작업을 하는 것이다. UNIX에서는 이러한 방식을 직접 입/출력(direct I/O)이라고 한다.

 

메모리 매핑(memory mapped) 파일 접근은 블록 장치 위의 계층으로 구현할 수 있다. 메모리 매핑 파일 접근이란 실제로 장치를 읽거나 쓰는 명령을 사용하는 대신 메모리의 특정 번지를 읽거나 쓰는 명령으로 파일 입/출력을 대신하는 방식이다. 키보드는 문자 스트림(character stream) 인터페이스를 통해 접근되는 장치의 예이다.

 

 

[네트워크 장치(Network Device)]

네트워크 인터페이스는 소켓(socket)인터페이스 이다. 원격지의 응용 프로그램이 이 소켓으로 접속을 완료하였는지 알아보고 연결이 되었으면 패킷(packet)을 주고 받도록 해 준다.

 

 

[클록과 타이머(Clock and Timer)]

대부분의 컴퓨터들은 하드웨어 클록과 타이머를 가지고 세 가지 기본적인 기능들을 제공한다.

  • 현재 시간을 제공
  • 경과된 시간을 제공
  • T 시각이 되면 X 오퍼레이션을 실행

 

이것은 어느 시간만큼 경과되면 인터럽트를 발생 시키도록 설정 할 수 있으며 이 과정을 한 번 또는 주기적으로 인터럽트를 발생하도록 설정 할 수 있다.

 

이 메커니즘은 스케줄러가 시간이 종료되면 현재 진행중인 프로세스로부터 CPU를 빼앗기 위해 사용되며 디스크 입/출력 서브시스템이 변경된 캐시 버퍼(dirty cache buffer)를 주기적으로 디스크에 쏟아내는 (flushing)데 사용되고 네트워크 서브시스템이 네트워크 혼잡이나 오류로 인해 어떤 작업을 취소하는데도 사용된다.

 

운영체제는 가상 클록을 흉내냄으로써 타이머 하드웨어 채널 수보다 더 많은 타이머 관련 요청들을 지원할 수도 있다. 하드웨어 클록의 틱에 의해 생성되는 인터럽트율은 초당 18~60틱 사이다. 간격이 촘촘하지 못한 이유는 타이머 하드웨어 자체에도 이유가 있지만 정밀하기 위한 가상 클록을 유지하는데 오버헤드가 매우 크기 때문이다.

 

따라서 대부분의 컴퓨터는 클록을 카운터로 구현한다. 카운터 값은 레지스터로부터 읽을 수 있도록 하여 정밀도를 높인다.

 

 

[봉쇄형과 비봉쇄형 입/출력(blocking and Nonblocking I/O)]

  • 봉쇄형 : 입/출력이 즉시 완료 될 수 없을 경우 응용프로그램은 봉쇄 상태로 들어간다. 즉 실행 큐로부터 대기 큐로 옮겨진다. 이후 수행 될 때 대기 큐에서 실행 큐로 옮겨지며 응용 프로그램은 입/출력 시스템 호출이 되돌려준 값을 받아 사용하게 된다.
  • 비봉쇄형 : 연산과 입/출력간의 중첩을 최대한 도모하기 위해 다중 스레드 방식으로 작성한다. 예를 들면 연산(렌더링)을 하는 동안 입출력 작업(마우스 움직임 등)을 할 수 있다. 비봉쇄형은 프로그램을 멈춰두지 않고 곧장 돌아오며 몇 개의 바이트가 전송되었는지를 알려주는 복귀값을 되돌려 준다.

 

비봉쇄형 시스템 호출의 대안으로 비동기식(asynchronous)시스템 호출이 있다. 비동기식 호출도 비봉쇄형과 같이 즉각 복귀 한다.

 

 

 

[참고자료]

Operating System Concepts / 홍릉과학출판사

 

 

57_폴링(Polling), 인터럽트(Interrupt), DMA(Direct Memory Access)

 

[폴링(Polling)]

호스트와 입/출력 하드웨어 사이의 프로토콜은 복잡하지만 기본적인 핸드셰이킹(hand shaking) 개념은 간단하다. 제어기는 상태 레지스터의 busy 비트를 통해 자신의 상태를 나타낸다. 제어기는 바쁠 때는 busy 비트를 1로 설정하고 다음 명령을 받아들일 준비가 되어 있을 경우에는 busy 비트가 0으로 변경한다.

 

호스트는 다음과 같은 방법으로 핸드셰이킹을 통해 제어기와 협력하면서 포트를 통해 출력을 쓴다.

호스트가 반복적으로 (소거될 때까지) busy 비트를 검사한다.

호스트가 명령 레지스터에 쓰기 비트(write bit)를 설정하고 출력(data out)레지스터에 출력할 바이트를 쓴다.

  1. 호스트가 명령어 준비 완료 비트(command ready bit)를 설정한다.
  2. (입/출력 하드웨어) 제어기가 명령어 준비 완료 비트가 설정된 것을 알아 차렸을 때 자신의 busy 비트를 설정한다.
  3. (입/출력 하드웨어) 제어기는 명령어 레지스터를 읽고 write 명령어임을 알게 된다. 출력 레지스터를 읽어 해당 바이트를 가져와 해당 하드웨어 장치로 출력한다.
  4. (입/출력 하드웨어) 제어기는 명령어 준비 완료 비트를 소거하고 입/출력이 끝났음을 알리기 위해 busy 비트를 소거한다.

 

이 루프는 매 바이트마다 반복되며 1단계에서 호스트는 폴링(polling)을 하게 된다.

 

 

[인터럽트(Interrupt)]

입/출력 장치가 CPU에게 자신의 상태 변화를 통보하는 하드웨어 기법을 인터럽트(interrupt)라 한다. CPU하드웨어는 인터럽트 요청 라인(interrupt request line)이라고 불리는 선을 하나 갖는데 CPU는 매 명령어를 끝내고 다음 명령어를 수행하기 전에 인터럽트 요청 라인을 검사한다.

  1. 입/출력 하드웨어 제어기가 이 요청 라인에 신호를 보낸다.
  2. CPU가 각종 레지스터 값과 상태 정보를 저장한 다음 메모리 상의 인터럽트 핸들러 루틴으로 이동 한다.
  3. 인터럽트 핸들러는 인터럽트의 발생 원인을 조사하고 필요한 작업을 수행 한 후 CPU를 인터럽트 이전 상태로 되딜리는 명령을 수행 한다.
  4. 핸들러는 입/출력 장치를 서비스함으로써 이 인터럽트를 처리해 준다.

 

 

대부분의 CPU 들은 두 종류의 인터럽트 요청 라인을 가진다.

  • 마스크 불가 인터럽트(nonmaskable interrupt) : 회복 불가능한 메모리 에러와 같은 이벤트를 위해 사용
  • 마스크 가능 인터럽트(maskable interrupt) : 필요 시 인터럽트 기능을 잠시 중단시켜 놓을 수 있는 기능으로 장치 제어기가 서비스를 요청하기 위해 사용한다. 인터럽트 되어서는 안 되는 주요 명령 시퀀스를 수행하기 전에 CPU가 끌 수 있다.

 

인터럽트 기법은 보통 주소라고 하는 하나의 작은 정수를 받아 들이는데 이 정수는 특정 인터럽트 핸들링 루틴을 선택하기 위해 사용되며 대부분의 구조에서 이 주소는 인터럽트 벡터라고 불리는 테이블의 오프셋으로 사용된다.

 

컴퓨터는 인터럽트 벡터 내에 있는 주소들보다 더 많은 수의 장치를 갖고 있다. 이러한 문제를 해결하구 위해 인터럽트 사슬(chaining) 기술을 사용한다. 인터럽트 사슬화에는 인터럽트 벡터의 각 원소들이 여러 인터럽트 핸들러들로 이루어진 리스트의 헤더를 가리키고 있다. 인터럽트가 발생하면 해당 핸들러가 찾아질 때까지 리스트 상의 핸들러들을 하나씩 검사하게 된다.

 

인터럽트 기법은 인터럽트 우선순위 수준(interrupt priority level)의 구현을 가능하게 한다. 이 기법은 CPU가 모든 낮은 우선순위 인터럽트를 일일이 마스크 오프(mask off)시키지 않더라도 자동적으로 높은 우선순위 인터럽트가 낮은 순위 인터럽트의 실행을 선점(preempt) 할 수 있게 한다.

 

 

[DMA (Direct Memory Access)]

CPU가 상태 비트를 반복적으로 검사하면서 1 바이트씩 옮기는 입/출력 방식을 PIO(Programmed I/O)라고 한다. 많은 컴퓨터들은 CPU의 PIO 작업 중 일부를 DMA(Direct Memory Access) 제어기라고 불리는 특수 처리기에 위임함으로써 CPU의 일을 줄여 준다.

 

DMA 전송을 시작시키기 위해서 호스트는 메모리에 DMA 명령 블록을 쓴다. 이 블록에는 전송할 자료가 있는 곳의 포인터와 전송할 장소에 대한 포인터, 그리고 전송될 바이트 수를 기록해 놓는다. 그리고 CPU는 이 DMA명령 블록의 주소를 DMA에게 알려주고 자신은 다른 일을 한다. DMA는 CPU의 도움 없이도 자신이 직접 버스를 통해 DMA 명령 블록을 접근하여 입/출력을 수행하게 된다.

 

DMA 제어기와 장치 제어기(Device controller)간의 핸드셰이킹은 DMA-request와 DMA-acknowledge라고 불리는 두 개의 선을 통해 수행 된다.

  1. 장치 제어기는 전송할 자료가 생기면 DMA-request 선에 신호를 보낸다.
  2. 이 신호를 받으면 DMA 제어기가 메모리버스를 얻어 거기에 원하는 주소를 올려놓고 DMA-acknowledge 선에 신호를 보낸다.
  3. 장치 제어기가 DMA-acnkowledge 신호를 받으면 제어기는 한 워드를 메모리로 전송하고 DMA-request 신호를 제거 한다.
  4. 전송이 완전히 끝나면 DMA 제어기는 CPU에게 인터럽트를 건다.

 

 

DMA가 메모리 버스를 점유중이면 비록 CPU는 주 캐시와 보조 캐시에 있는 자료는 접근할 수 있지만 일시적으로 주 메모리에 있는 자료는 접근하지 못한다. 이러한 사이클 스틸링(cycle stealing)은 CPU의 속도를 저하시키지만 입/출력 작업을 DMA로 넘기는 것은 전체적으로 시스템 성능을 향상 시킨다.

 

DMA를 사용할 때 물리적 주소를 사용하지만 DVMA(직접 가상 메모리 접근, direct virtual memory access)을 사용하기도 한다. DVMA를 사용하면 CPU나 메모리의 개입이 없어도 가상 주소로 두 개의 메모리 매핑(memory mapped) 장치간에 자료를 전송 할 수가 있게 된다.

 

[참고자료]

Operating System Concepts / 홍릉과학출판사

 

 

56_입/출력 시스템 (I/O System)

 

컴퓨터에서 주로 하는 작업이 연산작업과 I/O 작업 이다. 간단한 웹서핑 등의 작업은 연산보다는 I/O 작업이 더 많이 발생하기도 한다. 이처럼 다양한 입출력 장치들은 각각의 특성을 보이기 때문에 각각의 특성에 맞는 제어가 필요하다.

 

이처럼 다양한 제어 방법들이 커널의 입/출력 서브시스템(I/O Subsystem)을 형성하며 이들은 커널의 다른 부분이 입/출력 장치를 관리하는 복잡한 일에 신경을 쓰지 않게 해준다.

 

장치 드라이버는 모든 하드웨어를 일관된 인터페이스로 표현해 주며 이러한 인터페이스를 그보다 상위층인 커널의 입/출력 서브시스템에게 제공해 준다. 이는 시스템 호출이 응용과 운영체제간에 표준적인 인터페이스를 제공하는 것과 유사하다.

 

하드웨어 장치는 케이블을 통하거나 무선으로 신호를 보냄으로써 컴퓨터 시스템과 통신한다. 이들 장치는 포트(Port)라 불리는 연결점을 통해 컴퓨터와 접속한다. 하나 이상의 장치들이 공동으로 여러 선(wire)들을 사용한다면 이러한 선을 버스(bus)라 부른다. 버스는 회선의 집합으로 메시지를 주고 받을 수 있는 정한 프로토콜(protocol)까지를 포함한다.

 

장치 A->B, B->C, C->컴퓨터의 포트에까지 연결되어 있으면 데이지 체인(daisy chain)이라고 부른다. 데이지 체인은 통상 하나의 버스처럼 동작 한다.

 

버스는 컴퓨터 구조에서 널리 사용되며 제어기(controller)는 포트나, 버스나, 입출력 장치를 제어하는 전자 회로의 집합체이다. 모든 제어기는 레지스터를 가지고 있다. 본체의 처리기는 이들 제어기의 레지스터에 비트 패턴을 쓰거나 읽음으로써 입/출력을 수행 한다.

 

 

입/출력 명령은 한 바이트나 워드를 어떤 입/출력 포트 주소로 전달 하도록 지정한다. 입출력 명령은 해당 장치에 맞는 버스 회선을 선택하여 장치 레지스터로 비트들을 보내거나 읽어오도록 한다. 또 다른 방법으로는 장치 제어기 레지스터를 처리기의 주소 공간으로 매핑하여 사용한다. 이러한 방식을 memory mapped I/O 라고 한다. 이 경우 각 주변 장치 레지스터들은 메모리 주소와 일대일 대응 된다. CPU가 이 주소에 단순히 읽거나 쓰는 것은 곧 이 장치들에 대한 입/출력 요청을 의미하게 된다.

 

입/출력 포트는 보통 4개의 레지스터로 구성되는데 상태( status), 제어(control), 입력(data in), 출력(data out) 레지스터 들이다.

  • 상태 레지스터 : 호스트가 읽는 용도이며 이 비트들은 현재의 명령이 완료 되었는지, 입력 레지스터로부터 한 바이트를 읽어도 되는지, 그리고 에러가 있었는가와 같은 상태들을 보고 한다.
  • 제어 레지스터 : 호스트가 주변 장치에게 입/출력 명령을 내리거나 장치의 모드를 변경하기 위해 쓰기를 수행하는 대상
  • 데이터 입력 레지스터 : 호스트가 입력을 얻기 위해 읽기를 수행한다.
  • 데이터 출력 레지스터 ; 호스트가 자료를 출력하기 위해 쓰기를 수행 한다.

 

입력 및 출력 레지스터 등의 자료 레지스터들은 보통 1~4 바이트 이다. 어떤 제어기들은 이러한 자료 레지스터의 크기를 확장하기 위해 FIFO 칩들을 제공 한다. FIFO칩들은 자료의 발생이 돌발적으로 많이 지는 상황이 발생하더라도 대응 할 수 있다.

 

[PC에서 장치 입/출력 포트 위치]

입/출력 주소 범위(16진수)

장치

000 - 00F

DMA 제어기

020 – 021

인터럽트 제어기

040 – 043

타이머

200 – 20F

게임 제어기

2F8 – 2FF

직렬 포트(2차)

320 – 32F

하드 디스크 제어기

378 – 37F

병렬 포트

3D0 – 3DF

그래픽 제어기

3F0 – 3F7

디스켓 드라이브 제어기

3F8 – 3FF

직렬 포트 (메인)

 

 

[참고자료]

Operating System Concepts / 홍릉과학출판사

 

 

55_디스크 스왑 공간 관리(Disk Swap Space Management)

 

스와핑은 프로세스 전체를 디스크나 메인 메모리로 옮기는 작업으로 사용 가능한 물리 메모리가 매우 작을 때 활성화 되며 사용 가능 메모리를 만들기 위해 프로세스들은 메모리에서 스왑 공간으로 이동 된다. 대부분의 현재 운영체제들은 이러한 방식이 아닌 스와핑과 가상 메모리 기법을 이용하여 페이지를 스왑한다.

 

스왑 공간 관리는 운영체제가 수행하는 저수준 작업으로 가상 메모리는 디스크를 주 메모리의 스왑 공간으로 사용한다. 디스크는 메모리보다 느리므로 스왑 공간의 설계는 매우 중요하다.

 

[스왑 공간 사용 (Swap Space Use)]

사용하는 메모리 관리 알고리즘에 따라 스왑 공간은 운영체제마다 다양한 방법들로 운영된다. 스왑 공간은 예상보다 크게 잡는 것이 안전하다. 시스템을 운영하다가 스왑 공간이 바닥나면 프로세스들을 도중에 중단시켜야 하고 최악의 경우에는 시스템 전체가 손상될 수 있다.

  • Solaris는 가상 메모리 크기에서 페이징이 가능한 물리 메모리를 뺀 만큼을 제안한다.
  • Linux는 물리 메모리 크기에 두 배를 제안하나 현재 대부분의 Linux 시스템들은 그보다 작은 스왑 공간을 사용한다.

 

Linux를 포함한 여러 운영체제는 여러 개의 스왑 공간을 가질 수 있다. 이들 스왑 공간들은 보통 여러 다른 디스크에 할당되어 페이징과 스와핑에 대한 I/O부하를 I/O 디바이스에 분산한다.

 

[스왑 공간 위치 (Swap Space Location)]

스왑 공간은 두 군데 있을 수 있다. 일반 파일 시스템이 차지하고 있는 공간 안에 만들 수도 있고 별도의 파티션을 만들어 사용할 수도 있다.

 

스왑 공간이 만일 하나의 커다란 파일이라면 통상 파일 시스템 루틴을 사용하여 스왑 공간을 생성/삭제하고 스왑 공간을 관리 할 수 있다. 이 방법은 구현하기 쉬우나 스왑 할 때마다 디렉토리 구조와 디스크 할당 자료 구조를 거쳐야 하기 때문에 추가 액세스로 인한 비효율이 발생한다.

 

스왑 공간을 일반 파일이나 디렉토리는 저장되지 않는 별도의 비가공(raw) 파티션에 위치하는 경우 공간 효율성은 상대적으로 낮지만 스왑 공간은 파일 시스템 보다 자주 접근 되기 때문에 속도 효율성에는 많은 이득이 있다. 또한 내부 단편화 문제에서도 스왑 공간안에 있는 자료는 일반 파일보다 짧은 시간 동안만 존재하기 때문에 큰 문제가 되지 않는다.

 

스왑 공간은 부트시에 초기화 되어 단편화 현상은 오래 남지 않는다. 디스크 파티션을 나눌 때 별도의 파티션을 만들어 스왑 공간을 만들어 사용한다. 스왑 공간을 늘리려면 파티션 작업을 새로 하거나 별도의 파티션에 새로운 스왑 공간을 추가해야 한다.

 

 

[Solaris와 Linux 스왑 공간 관리]

Solaris는 가상 메모리 페이지가 처음 생성되었을 때가 아닌 한 페이지가 물리 메모리에서 쫓겨날 때 스왑 공간을 할당한다. 이 방법은 현재의 시스템이 예전보다 큰 물리 메모리를 가지고 있기 때문에 더 좋은 성능을 보여준다.

 

Linux는 스왑 공간을 익명 메모리 혹은 다수의 프로세스들이 공유하는 메모리 영역에만 사용한다. 이런 면에서 Solaris와 유사하다. Linux는 한 개 이상의 스왑 영역을 허용하며 일반 파일 시스템 및 스왑 파티션에 위치할 수도 있다. 각 스왑 영역은 연속된 4KB의 페이지 슬롯(page slot)들로 구성되고 이 페이지 슬롯들에 스왑된 페이지들이 적재 된다. 각각의 스왑 영역은 스왑 맵(swap map)으로 연관 된다. 해당 카운터 값이 0이면 사용가능하고 0보다 큰 값은 스왑된 페이지들이 사용하고 있는 것을 나타낸다

 

[참고자료]

Operating System Concepts / 홍릉과학출판사

 

 

+ Recent posts