본문 바로가기
AI & CS 지식/컴퓨터구조

[혼공컴운] 8. 입출력장치

by rahites 2024. 9. 2.

입출력장치

1. 장치 컨트롤러와 장치 드라이버

컴퓨터를 이루고 있는 장치에는 본체만 있는 것이 아니다. 모니터, 키보드, 마우스 등 어쩌면 우리가 본체 성능보다 더 관심이 많기도 한 이러한 입출력 장치들을 이번 시간에는 알아보도록 하자. 이번 절에서는 특히 다양한 입출력 장치가 컴퓨터 내부와 어떻게 연결되고 소통하는지에 대해 공부한다.

 

장치 컨트롤러

입출력 장치는 아래의 2가지 이유때문에 CPU나 메모리보다 다루기가 어렵다.

 

1) 너무 다양한 입출력 장치의 종류

키보드, 모니터, USB 등 너무나도 많은 종류의 입출력 장치가 존재하기 때문에 정보를 주고받는 방식을 규격화하기가 어렵다.

 

2) 낮은 데이터 전송률

일반적으로 CPU나 메모리의 데이터 전송률은 높지만, 입출력 장치의 전송률은 낮다. 여기서 전송률(Transfer Rate)이란 데이터를 얼마나 빨리 교환할 수 있는지를 나타내는 지표이다. 특정 입출력 장치는 CPU보다 전송률이 높기도 하지만 결국 두 장치간 전송률이 비슷하지 않다는 것이 문제가 된다.

 

이러한 문제때문에 입출력 장치는 컴퓨터와 직접 연결되지 않고 장치 컨트롤러(Device Controller)라는 하드웨어를 통해 컴퓨터와 연결된다. 장치 컨트롤러는 입출력 제어기(I/O controller), 입출력 모듈(I/O module)이라고도 부른다. 장치 컨트롤러는 하나 이상의 입출력 장치와 연결되어 있으며 다음과 같은 역할을 수행한다.

  • CPU와 입출력장치 간의 통신 중개
  • 오류 검출
  • 데이터 버퍼링

일종의 번역기 역할을 수행한다고 생각할 수 있다.

데이터 버퍼링(Buffering) : 전송률이 높은 장치와 낮은 장치 사이에 주고받는 데이터를 버퍼라는 임시 저장 공간에 저장하여 전송률을 비슷하게 맞추는 방법
ex. 버퍼에 데이터를 조금씩 모았다가 한번에 내보냄, 데이터를 버퍼에 한 번에 많이 받은 후 조금씩 내보냄

 

입출력 장치와 컴퓨터를 연결해 주는 장치 컨트롤러는 아래 3가지 레지스터로 구성되어 있다(실제로는 더 복잡하지만 중요한 부품만 정리)

  • 데이터 레지스터
  • 상태 레지스터
  • 제어 레지스터

데이터 레지스터는 CPU와 입출력장치 사이에 주고받을 데이터를 저장한다. 데이터 버퍼링을 수행하는 버퍼 역할을 하며 주고받는 데이터가 많은 입출력장치에서는 레지스터 대신 RAM을 사용하기도 한다.

 

상태 레지스터는 입출력 장치가 입출력 작업을 할 준비가 되었는지, 입출력 작업이 완료되었는지 등의 상태 정보를 저장한다.

 

제어 레지스터는 입출력장치가 수행할 내용에 대한 제어정보와 명령을 저장한다.

 

장치 드라이버

장치 드라이버는 장치 컨트롤러의 동작을 감지하고 제어하며 장치 컨트롤러가 컴퓨터 내부와 정보를 주고받을 수 있게 하는 프로그램이다. 장치 컨트롤러가 입출력장치를 연결하기 위한 하드웨어 통로라면 장치 드라이버는 소프트웨어적인 통로 역할을 수행한다.

장치 드라이버는 "특정 입출력 장치는 이러이러한 순서로 실행시켜서 동작시키면 된다!"는 정보를 알고 있다.

 

기계식 키보드, 무선 헤드폰 동글 등 최근 다양한 입출력 장치들이 자사만의 장치 드라이버를 배포하고 있다.

 

2. 다양한 입출력 방법

입출력 작업을 수행하기 위해선 CPU와 장치 컨트롤러가 정보를 주고받아야 한다. 이번 절에서는 CPU와 장치 컨트롤러가 어떤 방식으로 정보를 주고 받는지에 대해 알아보자.

 

CPU와 장치 컨트롤러가 정보를 주고받는 방법에는 크게 3가지가 존재한다.

 

1. 프로그램 입출력

프로그램 입출력은 프로그램 속에 있는 명령어로 입출력장치를 제어하는 방법이다. CPU가 특정 프로그램의 명령어를 처리하다가 입출력장치 관련 명령어를 만났을 때, 입출력장치에 연결된 장치 컨트롤러와 상호작용하며 입출력 작업을 수행하는 방식이다.

예시 : "메모리에 저장된 정보를 하드 디스크에 백업하자"
1. (백업한다는 말은 새로운 정보를 쓴다는 말이기에) CPU는 하드 디스크 장치 컨트롤러제어 레지스터에 쓰기 명령을 보냄
2. 하드 디스크 장치 컨트롤러는 하드 디스크가 쓸 준비가 되었는지 확인하고 상태 레지스터에 표시
3. 하드 디스크가 준비되었음을 CPU가 알게되면(수시로 상태 레지스터를 체크) 백업할 메모리의 정보를 데이터 레지스터에 씀

 

물론 위 작업을 위해서는 CPU가 장치 컨트롤러의 레지스터 위치(주소)를 알아야 한다. 이를 위해 장치 컨트롤러의 주소를 메모리에 저장하는 방식에는 2가지가 존재하는데 첫번째는 메모리 맵 입출력 방식이다. 

 

메모리 맵 입출력은 메모리에 접근하기 위한 주소 공간과 입출력장치에 접근하기 위한 주소 공간을 하나의 주소 공간으로 간주하는 방법이다. 그리고는 특정 장치 컨트롤러특정 레지스터특정 주소에 할당한다. 따라서 메모리 맵 입출력 방식에서는 메모리에 접근하는 명령어와 입출력장치에 접근하는 명령어에 차이가 없다. 특정 주소를 읽으라는 명령어를 실행했을 때 해당 주소가 메모리를 나타낸다면 메모리에, 입출력장치를 나타낸다면 입출력장치에 접근하면되기 때문이다.

 

두번째 방식은 고립형 입출력 방식이다. 이는 메모리를 위한 주소 공간과 입출력장치를 위한 주소 공간을 분리하여 사용하는 방식이다. 제어 버스에 메모리 읽기/쓰기를 위한 선입출력장치 읽기/쓰기를 위한 선이 따로 있어 각각의 장치에 맞게 주소 공간을 활용할 수 있다. 따라서 고립형 입출력 방식에서는 입출력장치 읽기/쓰기를 위한 선을 활성화 시키는 전용 명령어를 사용한다.

 

2. 인터럽트 기반 입출력

인터럽트는 4장에서 배웠던 것처럼 CPU가 입출력장치에 처리할 내용을 명령하고 입출력장치가 해당 명령을 수행하는 동안 CPU가 다른 일을 수행하기 위해 사용한다. 사실 이러한 인터럽트는 장치 컨트롤러에서 발생하는데, 입출력 장치에서 작업이 마무리 되었다면 장치 컨트롤러에서 CPU로 인터럽트 요청을 보내는 식이다.

인터럽트와 비슷한 개념으로 폴링(Polling)이 존재한다. 이는 입출력장치의 상태가 어떤지, 처리할 데이터가 있는지를 주기적으로 확인하는 작업을 의미한다. 이는 주기적으로 확인해야하기 때문에 인터럽트 방식보다 CPU의 부담이 더 크다는 단점이 있다.

 

우리는 일반적으로 여러 입출력장치를 같이 사용하는데, 그렇다면 여러 입출력장치에서 동시에 인터럽트가 발생하였다면 어떻게 될까?

 

처리하기 가장 쉬운 방법은 인터럽트가 발생한 순서대로 인터럽트를 처리하는 것이다. 하지만 현실적으로 모든 인터럽트를 순차적으로 처리할 수 없기에 CPU는 인터럽트 간의 우선순위를 고려하여 우선순위가 높은 인터럽트 순으로 여러 인터럽트를 처리한다.

 

특정 인터럽트를 처리하기 위해 플래그 레지스터 속 인터럽트 비트가 비활성화되어있더라도 무시할 수 없는 인터럽트인 NMI(Non-Maskable Interrupt)가 발생한 경우 CPU는 해당 인터럽트를 먼저 처리한다.

 

우선순위를 반영하여 여러 인터럽트를 처리하는 방식은 여러가지가 있지만 많은 컴퓨터에서는 프로그래머블 인터럽트 컨트롤러(PIC)를 사용한다. 이는 여러 장치 컨트롤러에 연결되어 장치 컨트롤러에서 보낸 하드웨어 인터럽트 요청들의 우선순위를 판별한 뒤 CPU에 현재 처리해야 할 인터럽트를 알려준다.

PIC (Wikipedia)

위 사진처럼 PIC에는 여러 핀이 존재하는데, 각 핀을 통해 CPU에 하드웨어 인터럽트 요청을 보낼 수 있다. PIC는 인터럽트간의 우선순위를 판단하는 역할또한 수행한다(NMI의 경우 가장 우선이기 때문에 순위 판단 X). PIC의 다중 인터럽트 처리 과정은 아래와 같다.

  1. PIC가 장치 컨트롤러에서 인터럽트 요청 신호를 받아들임
  2. PIC가 인터럽트 우선순위를 판단한 뒤 CPU에 처리해야 할 인터럽트 요청 신호를 보냄
  3. CPU가 PIC에 인터럽트 확인 신호를 보냄
  4. PIC가 데이터 버스를 통해 CPU에 인터럽트 벡터를 보냄
  5. CPU는 인터럽트 벡터를 통해 어떤 인터럽트를 수행해야 하는지 알게되고 해당 장치의 인터럽트 서비스 루틴을 실행함

일반적으로는 더 많고 복잡한 장치의 인터럽트를 관리하기 위해 여러개의 PIC를 계층적으로 구성하여 사용한다.

 

3. DMA 입출력

위에서 소개한 프로그램 기반 입출력방식과 인터럽트 기반 입출력 방식은 이동하는 데이터가 CPU를 거쳐서 연산량이 늘어난다는 단점을 가진다. 따라서 등장한 방식이 입출력장치와 메모리가 CPU를 거치지 않고도 상호작용할 수 있는 DMA(Direct Memory Access)이다. DMA 방식으로 입출력하기 위해서는 시스템 버스에 DMA 컨트롤러 하드웨어를 연결해야 한다.

※ CPU를 거치면 연산량이 많아지는 이유
입출력장치 데이터를 메모리에 저장하는 경우 : 장치 컨트롤러 -> CPU 레지스터 -> 메모리
메모리 데이터를 입출력장치에 내보내는 경우 : 메모리 -> CPU 레지스터 -> 장치 컨트롤러

 

DMA 방식의 입출력 과정은 아래와 같다.

  1. CPU가 DMA 컨트롤러에 입출력장치의 주소, 수행할 연산, 읽거나 쓸 메모리 주소 등의 정보를 주어 입출력 작업을 명령
  2. DMA 컨트롤러가 CPU 대신 장치 컨트롤러와 상호작용하며 입출력 작업을 수행. 이 때 필요하다면 메모리에 직접 접근하여 정보를 읽거나 씀
  3. 입출력 작업이 끝나면 DMA 컨트롤러는 CPU에 인터럽트를 걸어 작업이 끝났음을 알림

이러한 방식을 통해 CPU는 입출력의 시작과 끝에만 관여할 수 있어 작업 부담을 훨씬 줄일 수 있다.

어찌 보면 수행해야 하는 업무를 아래 기업에게 하청을 맡기는 것 같다.

 

위 과정에서 DMA 컨트롤러가 메모리에 접근하는 경우 시스템 버스를 사용한다. 이 때 시스템 버스는 공용 자원이기 때문에 DMA 컨트롤러는 CPU가 시스템 버스를 사용하지 않을때에만 시스템 버스를 통해 메모리에 접근한다(사이클 스틸링). 하지만 이러한 상황이 너무 많이 발생한다면 그만큼 CPU가 시스템 버스를 이용하지 못한는 문제가 발생한다.

 

이러한 문제를 극복하기 위해 DMA 컨트롤러와 장치 컨트롤러들을 입출력 버스라는 별도의 버스에 연결하여 사용한다. 이를 통해 기존 시스템 버스를 사용하던 "메모리 -> DMA 컨트롤러 -> 장치 컨트롤러" 순서 중 "메모리 -> DMA 컨트롤러" 구간에만 시스템 버스를 사용할 수 있다.

 

입출력 버스에는 PCI(Peripheral Component Interconnect) 버스, PCI Express(PCle) 버스 등 여러 종류가 존재한다.

 

PCI와 PCIe

https://www.fibermall.com/ko/blog/pci-e-8-things-need-to-know.htm?srsltid=AfmBOorc_5NhWklOi2UM0YLMRcxlfazoANhjc3QPqokSuIHZf0VPPPVP

 

PCI-E: 초기에 알아야 할 8가지 사항

PCIe를 통해 신호가 상호 연결되어 CPU가 다양한 외부 장치와 통신할 수 있도록 하기 때문에 PCIe는 서버 시스템에서 가장 중요한 버스입니다. 데이터의 각 비트를 소형 자동차와 비교하면 PCIe 버스

www.fibermall.com

https://youtu.be/4p9rF193PkU?si=uHbYyK6sM1hln2gg

댓글