메모리

메모리는 컴퓨터에서 프로그램과 데이터를 저장하는 공간으로, 앞서 3장에서 다룬 RAM과 같은 반도체 메모리뿐 아니라 캐시, 디스크 등까지 포함하는 개념이다. 메모리는 흔히 주소가 부여된 연속적인 저장 공간으로 추상화된다. 다시 말해, 메모리는 일정한 크기의 저장 단위들이 한 줄로 쭉 늘어선 형태이며, 각 단위마다 0, 1, 2, ...와 같은 고유한 주소 번호가 매겨져 있다. 이는 마치 집이 늘어서 있는 거리에 집마다 번지가 있는 것과 비슷하다. 프로그램에서 특정 데이터를 읽거나 쓰고 싶을 때는 그 데이터가 위치한 메모리 주소를 지정하면 된다.

메모리의 주소 공간(address space) 크기는 컴퓨터의 설계(아키텍처)에 따라 다르다. 예를 들어 32비트 컴퓨터에서는 한 번에 다룰 수 있는 주소의 크기가 32비트이므로, 이론상 최대 2^32개의 주소(약 43억 개, 보통 4GB까지)를 사용할 수 있다. 이때 일반적으로 메모리를 4바이트(32비트) 단위로 묶어서 주소를 부여한다. 한 주소가 가리키는 단위가 4바이트이므로, 32비트 CPU는 한 번에 4바이트씩 데이터를 처리하는 것이 기본이다. 마찬가지로 64비트 컴퓨터에서는 메모리를 8바이트(64비트) 단위로 취급하며, 주소 공간도 훨씬 크다(이론상 16EB까지 가능하지만 실제로 그렇게 크진 않다). 이렇게 워드(word)라 불리는 기본 단위로 메모리를 구성하면 CPU가 효율적으로 데이터를 주고받을 수 있다.

만약 프로그램이 메모리에 접근할 때 이 정해진 단위 경계를 어긋나게 걸쳐서 데이터를 읽으려고 하면 어떻게 될까? 예를 들어 32비트 시스템에서 4바이트 경계에 정렬되지 않은 임의의 주소(예: 0x0003)에서 4바이트의 데이터를 읽으려고 하면, 데이터 일부는 한 워드에, 나머지는 다음 워드에 걸쳐 있게 된다. 이를 메모리 정렬(alignment)이 맞지 않는 미스얼라인드 접근이라고 한다. 일부 시스템에서는 이런 접근을 허용하지 않아 예외가 발생하거나, 허용하더라도 한 번에 처리할 수 없어 여러 번 나누어 읽는 등 성능이 저하된다. 따라서 효율적인 시스템에서는 데이터 구조를 메모리에서 규정된 단위(예: 4 또는 8바이트) 경계에 맞춰 저장하는 것이 중요하다.

컴퓨터에는 다양한 종류의 메모리 기술이 사용되며, 각각 용량과 속도, 비용 면에서 상이한 특성을 지닌다. 가장 빠른 메모리는 CPU 내부에 있는 레지스터와 캐시로, 매우 제한된 용량이지만 나노초 단위의 접근 속도를 제공한다. 주기억 장치인 DRAM 메모리는 속도는 조금 느리지만 수 기가바이트 이상의 큰 용량을 경제적으로 제공한다. SSD나 디스크와 같은 보조기억 장치는 속도는 밀리초 단위로 느린 대신 테라바이트 급의 대용량과 낮은 가격으로 데이터를 저장한다. 이처럼 메모리는 계층별로 속도 대 용량(또는 비용) 특성이 트레이드오프로 존재하며, 컴퓨터 시스템은 이들 다양한 메모리를 조합하여 자주 쓰이는 데이터는 빠른 곳에, 덜 자주 쓰이는 데이터는 느린 곳에 두는 방식으로 성능과 비용의 균형을 맞추고 있다.

입력과 출력 (I/O)

입력/출력(Input/Output, I/O) 장치는 컴퓨터 내부와 외부 세계 사이에 정보를 주고받는 통로이다. 사용자가 키보드나 마우스로 컴퓨터에 명령을 전달하거나, 컴퓨터가 모니터 화면이나 프린터로 결과를 내보내는 것이 모두 I/O의 예이다. 컴퓨터 본체에 연결된 이러한 장치들을 통틀어 I/O 장치 혹은 주변 장치(peripheral)라고 부른다. I/O 장치는 키보드, 마우스, 디스크 드라이브, 네트워크 카드, USB 장치, 센서 등 매우 다양하며, 각각의 통신 방법과 속성이 다를 수 있다.

초창기 대형 컴퓨터에서는 메모리와 I/O를 완전히 별개의 주소 공간으로 취급했다. 즉, 메모리 전용 주소와 I/O 전용 주소를 구분하여, CPU가 메모리에 접근할 때와 I/O 장치에 접근할 때 아예 다른 통로와 명령 체계를 사용하는 방식이었다. 이러한 방법을 분리된 I/O 공간이라고 할 수 있다. 그러나 현대의 컴퓨터, 특히 마이크로프로세서 기반 시스템들은 대개 메모리 맵드 I/O(memory-mapped I/O) 구조를 사용한다. 이는 I/O 장치를 메모리의 특정 주소 범위에 할당하여, CPU가 마치 메모리에 읽고 쓰듯이 I/O 장치를 제어하도록 만든 것이다. 예를 들어 특정 주소를 쓰면 그 주소에 대응되는 장치의 레지스터에 값이 전달되고, 특정 주소를 읽으면 장치로부터 상태나 데이터가 오는 식이다. 이렇게 하면 CPU 입장에서는 특별한 I/O 명령 없이도 메모리 접근만으로 장치를 다룰 수 있어 구조가 단순해진다. 현대 컴퓨터의 주소 공간이 과거에 비해 충분히 크기 때문에, 일부 영역을 메모리가 아닌 I/O 장치와 연결해도 무리가 없기 때문이다.

또한 여러 종류의 I/O 장치를 일관된 방식으로 연결하고 제어하기 위해, 컴퓨터 설계상 표준 버스 인터페이스나 슬롯들이 제공된다. 예를 들어 PC에서는 PCI/PCIe 버스USB 포트SATA 인터페이스 등 정해진 형태의 연결 규격이 있어서, CPU와 운영체제가 공통된 방식으로 다양한 장치들을 인식하고 다룰 수 있다. 이런 표준화된 I/O 구조 덕분에 새로운 주변장치를 추가하더라도 시스템에 큰 변경 없이 사용할 수 있으며, 확장성과 호환성이 높아진다.

중앙 처리 장치 (CPU)

산술 논리 장치 (ALU)

시프트(Shift) 회로

ALU와 별도로 CPU에는 비트 이동(shifting)을 전담하는 시프터(shifter) 회로가 포함되는 경우가 많다. 시프트 연산은 이진수의 비트를 왼쪽이나 오른쪽으로 한 칸씩 밀어내는 연산이다. 예를 들어 왼쪽 시프트를 한 번 수행하면, 8비트 값 0001 0110이 0010 1100으로 바뀌는 식이다. 가장 왼쪽의 비트는 밀려 나가 버리고, 새로 생긴 가장 오른쪽 비트에는 0을 채운다. 오른쪽 시프트의 경우는 반대로 0001 0110 -> 0000 1011 (오른쪽 끝 비트는 버려지고 왼쪽에는 0 삽입) 이런 식으로 작동할 수 있다. 시프트 연산은 2의 곱셈이나 나눗셈을 빠르게 수행하거나, 특정 비트를 마스크(mask)하거나 추출하는 등 매우 유용한 비트 조작 기능이다. 간단한 회로로도 구현되지만, 고속으로 여러 비트를 동시에 밀어내려면 하드웨어 전용 장치가 있으면 효율적이므로, 많은 CPU에서 ALU와 별도로 시프터를 두어 한 번 클록으로 여러 비트를 이동시킨다. (일부 CPU에서는 ALU가 시프트 연산까지 포함해 처리하기도 한다.)