오늘은 컴퓨터의 핵심부품 4가지
CPU, 메모리, 보조기억장치, 입출력장치
중에서 마지막 핵심부품인 입출력장치에 대해서 배웠다.
입출력장치는 컴퓨터 외부에 연결되어 컴퓨터 내부와 데이터를 주고 받는 부품이다.
오늘 배운 내용은 어떻게 컴퓨터와 외부에 연결된 입출력장치가 데이터를 주고 받을 수 있는가에 대한 물음을 해결해준다.
오늘도 🏃♂️🏃♀️🏃🏃♀️🏃♂️🏃♀️
종류가 너무 많다.
CD-ROM, USB 메모리, 마우스, 프린트 등 종류가 너무 많고 기기마다 속도, 데이터 전송 형식도 다르다.
CPU와 메모리의 데이터 전송률과 입출력장치의 데이터 전송률이 서로 다르다.
데이터 전송률이란?
데이터를 얼마나 빨리 교환할 수 있는지를 나타내는 지표
예를 들어
어떤 사람은 1시간에 100개의 말을 할 수 있고 이해할 수 있지만 어떤 사람은 1시간에 1개의 말을 할 수 있고 이해할 수 있다.
위 예시와 같이 데이터 전송률이 다르면 서로 소통할 수 없다. 그래서 CPU, 메모리와 입출력장치 간의 통신은 어렵다.
장치 컨트롤러(device controller)
= 입출력 제어기 (I/O controller)
= 입출력 모듈(I/O module)
따라서 CPU,메모리와 입출력장치는 직접 소통하지 못하고 장치 컨트롤러라고 불리는 부품을 통해서 통신이 가능하다.
모든 입출력장치는 각자의 장치 컨트롤러를 통해 내부와 정보를 주고 받는다. 장치 컨트롤러는 하나 이상의 입출력 장치와 연결되어 있다.
CPU와 입출력 장치 간 통신을 중개(번역기)하면서 오류를 검출한다.
데이터 버퍼링을 한다.
데이터 버퍼링이란?
전송률이 높은 장치와 전송률이 낮은 장치 간 주고받는 데이터를 저장하는 곳을 버퍼(Buffer)라고 한다.
즉 버퍼에 저장하여 전송률이 다른 두 장치 간의 전송률을 비슷하게 맞춰주는 역할을 하는 것이 버퍼이다.
전송률이 낮은 장치에서 전송률이 높은 장치로 데이터를 보낼 때
버퍼에 조금씩 모아 저장한 후 한번에 전송률이 높은 장치로 보낸다.
반대로 전송이 높은 장치에서 전송률이 낮은 장치로 데이터를 보낼 때 한번에 많이 받아서 하나씩 전송률이 낮은 장치로 데이터를 내보내는 방법을 "데이터 버퍼링"이라고 한다.
장치 컨트롤러는 크게 3개의 레지스터로 구성되어져 있다.
사실 장치 컨트롤러만 연결되었다고 해서 바로 컴퓨터 내부와 통신할 수 있는게 아니다!
내가 어떤 프린터를 사서 노트북에 연결하면 바로 출력할 수 있는게 아니다. 삼성 프린터라면 삼성전자 사이트 자료실에 가서 그 제품명에 맞는 프로그램을 설치해야 한다.
(USB를 연결할 때 장치 드라이버를 설치하지 않는 이유는 운영체제가 기본으로 제공하는 장치 드라이버도 있기 때문이다.)
이 프로그램을 장치 드라이버라고 한다.
한 가지 더 생각해보면 설치하기 전에 운영체제를 선택하게 되어 있는데 그 이유를 장치 드라이버를 실행하는 주체가 운영체제이기 때문이다.
장치 드라이버는 장치 컨트롤러의 동작을 감지하고 제어함으로써 장치 컨트롤러와 컴퓨터 내부가 정보를 주고받을 수 있게 한다.
한 가지 방법만 선택해서 사용하는 것이 아니라 3가지 방법을 상황에 맞게 혼용해서 사용한다.
프로그램 입출력과 인터럽트 기반 입출력, DMA 입출력이 있으며 이 셋은 하나의 기준을 가지고 나눠진 방법이 아니다.
프로그램 입출력은 프로그램 속 명령어를 통해서 입출력장치와 통신하는 방법이고 인터럽트 기반 입출력은 인터럽트 신호를 통해서 통신하는 방법이다.
나머지 하나 DMA는 나머지 두 방법과 다르게 CPU가 메모리와 장치 컨트롤러 사이의 데이터 이동을 주도하는 것이 아니라 DMA 컨트롤러가 데이터 이동을 담당하게 하는 방법이다.
자세히 알아보자
기본적으로 프로그램 속 명령어로 입출력장치를 제어하는 방법
CPU가 프로그램 속 명령어를 실행하다가
"메모리에 저장된 정보를 하드 디스크에 백업한다"와 같은 입출력 명령어를 만나면 CPU는 입출력장치와 연결된 장치 컨트롤러와 상호작용하며 입출력 작업을 수행한다.
예시를 들어보자
CPU : 제어 레지스터에 쓰기 명령을 보낸다
장치 컨트롤러 제어 레지스터 : "쓰기"
입출력장치 : 상태 레지스터에 "준비 완료" 표시
CPU : 상태 레지스터를 주기적으로 읽으며 "준비 완료"상태 인지 확인하고 "준비 완료" 상태라면 백업할 메모리의 정보를 데이터 레지스터에 쓴다.
보면 CPU가 장치 컨트롤러의 레지스터 값을 읽으며 행동하고 있다.
이는 CPU가 장치 컨트롤어 레지스터의 주소를 알고 있어야 가능한 일이다.
하지만 CPU는 수많은 입출력장치의 장치 컨트롤러 레지스터의 주소를 알고 있을까?
어떻게 가능할까?
만약 가능하다면 어떤 형태의 명령어가 메모리 어디에 저장되어 있을까?
이를 가능하게 하는 방식은 크게 2가지 있다.
사실 여기서는 주소 공간이라는 개념이 등장한다.
주소공간과 메모리는 다른 개념인거 같아서 따로 찾아보았다.
주소공간에 대한 이해는 아직 부족하지만 간략하게 설명하면
멀티 프로그래밍의 시대(여러 프로그램을 동시에 실행하고 싶은)가 도래하면서 실행중인 프로그램을 짧고 빠르게 실행하는 시분할 형식을 도입했다. 마치 사용자가 느끼기에 동시에 실행되는 것처럼 보인다.하지만 이 방법의 단점은 실행 중인 프로세스의 중단 시점에서의 전체 데이터를 메모리에서 디스크 종류의 저장장치(물리적 저장장치)로 옮겨야 했다. 그래야 순서가 돌아올 때 복구해서 중단된 시점부터 시작할 수 있었기 때문이다. 그러나 이 과정에 너무 많은 시간적 비용이 들었다.
따라서 디스크에 저장하지 않고 프로세스를 그대로 메모리에 두는 운영 방식을 선택했다. 이 방식은 운영체제가 효율적으로 번갈아 프로스세를 실행하도록 시분할의 역할을 운영체제에 넘겨버린 것이다.
메모리 안에 여러 개의 프로세스가 존재한다는 것은 프로세스가 서로를 침범할 수 있는 보안의 위험이 발생할 수 있다는 것이다. 따라서 운영체제는 주소 공간이라는 개념을 도입한다
주소 공간이란
실행중인 프로그램이 가정하는 메모리의 모습이다.
실행중인 프로그램의 모든 메모리 상태를 가지고 있다.
마치 혼자 실제보다 큰 메모리 공간을 사용하고 있는 것처럼 느낀다.
다시 돌아와서 메모리 맵 입출력에 대해서 알아본다.
메모리 맵 입출력은
메모리에 접근하기 위한 주소 공간과 입출력장치에 접근하기 위한 주소 공간을 분리하지 않고 하나의 주소 공간을 같이 사용하는 방법이다.
예를 들어 1024개의 주소를 표현할 수 있는 주소 공간에 512개는 메모리에 접근하기 위한 주소 공간으로 사용하고 나머지를 입출력장치에 접근하기 위한 주소 공간으로 사용하는 것이다.
따라서 명령어도 입출력장치를 위한 명령어와 메모리 접근을 위한 명령어를 따로 분리해서 다르게 표현할 필요가 없다.
그냥 하나의 메모리를 사용하는 것처럼 표현할 수 있기 때문이다.
메모리 맵 입출력과 다르게
메모리 접근을 위한 주소공간과 입출력장치를 접근하기 위한 주소 공간이 분리되어 있다.
따라서 명령어로 다르다.
메모리 접근을 위한 주소 공간을 사용하는 명령어에는
"메모리 읽기/쓰기" 제어버스를 활성화시키는 명령어가 포함되어져 있다.
반대로 입출력장치 접근을 위한 주소 공간을 사용하는 명령어에는
"입출력장치 읽기/쓰기" 제어 버스를 활성화시키는 명령어가 포함되어져 있다.
메모리 맵 입출력 | 고립형 입출력 |
---|---|
메모리와 입출력장치 같은 주소 공간 | 다른 주소 공간 |
메모리 주소 공간 축소 | 축소되지 않음 |
같은 형태의 명령어 | 다른 형태의 명령어(전용 명령어가 있음) |
여기서 설명하는 인터럽트는 하드웨어에 의해서 발생하는 인터럽트로 CPU에게 입출력장치가 "끼어들어도 되나요?"라는 알람 신호를 보내는 것이다. 그러면 CPU는 스택에 하던 일을 백업해 두고 인터럽트 서비스 루틴을 실행한다. 완료된 후에는 백업해 둔 데이터를 불러와 중단되었던 프로그램을 다시 실행한다.
만약에 이 알람 신호가 없다면 CPU는 주기적으로 입출력장치의 상태를 확인해줘야 하기 때문에 낭비가 생긴다.
이와 같이 인터럽트를 기반으로 하는 입출력을 인터럽트 기반 입출력이라고 한다.
그러나 많은 입출력장치로부터 인터럽트 신호가 오고 인터럽트 사이에 우선 순위가 존재하기 때문에
이 우선순위를 반영하여 다중 인터럽트를 처리하는 컴퓨터 부품(하드웨어)이 있다.
PIC는 장치 컨트롤러부터 오는 입출력 장치의 우선순위를 반영해서 CPU에 보낸다.
따라서 전체적인 흐름은 아래와 같다.
인터럽트 중에서 가장 먼저 실행되는 가장 높은 우선순위를 갖는 인터럽트를 NMI(Non-Maskable Interrupt)라고 한다.
이 인터럽트는 플래그 레지스터의 인터럽트 비트가 비활성상태(CPU가 인터럽트 요청을 받는 것이 불가능)여도 실행된다.
앞서 배운
프로그램 입출력과 인터럽트 기반 입출력은
저장
입출력장치 ➡️ 장치 컨트롤러➡️ CPU의 레지스터➡️ 메모리
내보내기
메모리➡️ CPU 레지스터 ➡️ 장치 컨트롤러 ➡️ 입출력장치
위와 같이 CPU를 통해서 데이터의 이동이 가능했다.
하지만 CPU는 입출력장치의 명렁어만 수행하는 것이 아니라
매우 바쁘기 때문에
이 방법이 아니라 입출력장치가 CPU의 도움 없이 메모리 간의 데이터 이동이 가능하게 하는 방식이 없을까?
바로 DMA(Direct Memory Access)가 등장하였다.
DMA는 시스템 버스와 연결되어져 있는 DMA 컨트롤러를 통해서 메모리와 데이터를 주고 받는다.
앞서 DMA 컨트롤러는 시스템 버스와 연결되어져 있다고 했다.
시스템 버스는 공용 자원이어서 같이 사용할 수가 없다.
CPU가 사용할 때는 DMA 컨트롤러는 사용할 수 없고
DMA가 사용할 때 CPU는 사용할 수 없다.
CPU는 사용해야할 시기에 사용하지 못하는 경우가 발생한다.
그래서 DMA의 시스템 버스 이용을 사이클 스틸링(cycle stealing)이라고도 한다.
따라서 이 문제를 해결하기 위해 현대 대부분의 컴퓨터들은 장치 컨트롤러와 DMA 컨트롤러를 입출력 버스에 연결하는 방법을 사용하고 있다.
입출력 버스의 종류