개요
입출력 작업을 하기 위해선 CPU와 장치 컨트롤러가 데이터를 교환해야한다. 이때 어떤 방식으로 데이터를 교환할 수 있을까?
프로그램 입출력(programmed I/O)는 명령어로 입출력 장치를 제어하는 방법이다. CPU가 작업을 하는 도중 입출력 명령어를 만나면 CPU는 입출력장치에 연결된 장치 컨트롤러와 교류하며 입출력 작업을 수행한다.
메모리에 있는 데이터를 하드 디스크에 백업하는 상황을 가정해보자. 이는 아래 과정으로 나타낼 수 있다.
CPU는 하드 디스크 컨트롤러의 제어 레지스터에 쓰기 명령을 보낸다.
하드 디스크 컨트롤러는 디스크 상태를 확인한다. 디스크가 준비가 되었다면 하드 디스크 컨트롤러는 상태 레지스터에 준비가 되었다고 표시한다.
CPU는 상태 레지스터를 주기적으로 읽으며 준비 여부를 확인한다.
하드 디스크가 준비되었다면 백업할 메모리의 정보를 데이터 레지스터에 쓴다.
백업이 끝나지 않았다면 준비 여부를 다시 확인하고 백업 작업을 한다.
위와 같이 프로그램 입출력 방식의 입출력 작업은 CPU가 장치 컨트롤러의 레지스터 값을 읽고 쓰면서 이루어진다.
그런데 이때 CPU는 레지스터들의 주소를 어떻게 알 수 있을까?
여기에는 크게 두 가지 방식이 있다. 메모리 맵 입출력과 고립형 입출력이다.
메모리 맵 입출력(memory-mapped I/O)은 입출력 장치를 컴퓨터 메모리 공간으로 매핑하여 처리하는 방식이다. 입출력 장치가 메모리 주소 공간에 할당되어 있으므로 프로그램은 해당 메모리 주소에 데이터를 읽거나 쓰는 것처럼 주변 장치와 통신할 수 있다.
이때 주소 공간을 메모리에 접근하기 위한 공간과 입출력장치에 접근하기 위한 공간을 하나로 간주 한다.
예를 들어 1050개의 주소를 표현할 수 있는 컴퓨터가 있을 때 525개는 메모리 주소 , 나머지 525개는 장치 컨트롤러의 레지스터를 표현하기 위해 사용한다.
그래서 메모리 맵 입출력 방식에서는 메모리와 장치 컨트롤러 레지스터에 접근하는 명령어가 다르지 않아도 된다는 장점이 있다.
고립형 입출력(isolated I/O)는 메모리 맵 입출력과는 다르게 메모리의 주소 공간과 입출력장치의 주소 공간을 분리하는 방법이다.
이 방법은 제어 버스에 메모리 읽기/쓰기 선과 입출력장치 읽기/쓰기 선을 따로 두는 방식이다. 이 방식을 사용한다면 접근 명령어가 달라지지만, 마찬가지로 1050개의 주소를 표현할 수 있는 컴퓨터가 있을 때, 주소 공간을 메모리 공간과 장치 컨트롤러 레지스터 둘 다 1050개를 사용할 수 있다.
입출력 장치가 CPU에게 인터럽트 요청 신호를 보내면 CPU는 하던 일을 잠시 멈추고 인터럽트 서비스 루틴을 실행한 뒤 다시 하던 일로 돌아온다.
인터럽트 신호는 정확히 말하면 장치 컨트롤러에 의해 발생한다. 장치 컨트롤러가 입출력 작업을 끝낸 뒤 CPU에게 인터럽트 요청 신호를 보내면 CPU는 인터럽트 서비스 루틴을 실행한다.
이를 인터럽트 기반 입출력(interrupt-Driven I/O) 라고 한다.
그런데 만약, 여러 입출력장치에서 인터럽트가 동시에 발생하면 어떻게 처리해야할까?
간단히 생각하면, 인터럽트가 발생한 순서대로 처리하는 방법이 있다. 하지만 인터럽트 중에도 빨리 처리해야하는 인터럽트가 있다면 에로사항이 발생한다. 따라서 CPU는 인터럽트 간에 우선순위를 고려하여 우선순위가 높은 인터럽트 순으로 처리해야한다.
우선순위를 반영하여 다중 인터럽트를 처리하는 방법에는 여러 가지가 있다. 많은 컴퓨터에서는 프로그래머블 인터럽트 컨트롤러(PIC: Programmable Interrupt Controller)라는 하드웨어를 사용한다.
PIC은 여러 장치 컨트롤러에 연결되어 인터럽트 요청들의 우선순위를 판별한 뒤 CPU에게 알려주는 장치이다.
PIC의 다중 인터럽트 처리 과정은 아래와 같다.
일반적으로 더 많고 복잡한 장치들의 인터럽트를 관리하기 위해 PIC을 두 개 이상 계층적으로 구성한다.
이때, PIC이 무시할 수 없는 인터럽트인 NMI까지 우선순위를 판별하지 않는다.
입출력장치와 메모리 간의 데이터 이동은 CPU를 통하고, 이동하는 데이터도 항상 CPU를 거친다.
이렇게 된다면 CPU는 입출력장치를 위한 연산 때문에 시간을 많이 소모하게 된다. 하드 디스크 백업과 같은 대용량 데이터가 오간다면 더욱 그렇다.
그래서 DMA가(Direct Memory Access)가 등장하였다. 이는 이름 그대로 직접 메모리에 접근할 수 있는 입출력 기능이다.
DMA 입출력을 하기 위해선 시스템 버스에 연결된 DMA 컨트롤러하드웨어가 필요하다.
DAM 입출력 과정은 아래와 같다.
여기서 한 가지 더 생각해야할 문제가 있다. DMA는 시스템 버스로 데이터를 주고 받지만, 시스템 버스는 공용 자원이라 CPU와 동시 사용이 불가능하다. 그래서 DMA는 CPU가 시스템 버스를 이용하지 않을 때마다 조금씩 이용하거나, DMA가 일시적으로 CPU에게 허락을 구하고 시스템 버스를 집중적으로 이용한다.
위와 같이 시스템 버스를 동시에 사용한다면 작업들이 많이 뎌디다. DMA가 시스템 버스를 자주 사용하면 그만큼 CPU는 사용하지 못하게 될 것이다. 이러한 문제는 DMA와 장치 컨트롤러를 입출력 버스(input/output bus)라는 별도의 버스에 연결하여 해결할 수 있다.
이러면 장치 컨트롤러와 DMA간에 데이터를 전송할 때 시스템 버스를 이용하지 않아도 되므로 시스템 버스의 사용 빈도를 줄일 수 있다.
입출력 버스에는 PCI(Peripheral Component Interconnect) 버스 , PCI Express(PCIe) 버스 등 여러 종류가 있다.
좋은 글 감사합니다. 자주 올게요 :)