랜덤CS 시리즈는 CS에 관한 지식을 분야를 특정하지 않고 무작위로 산출하여 공부하고 작성하는 시리즈입니다.
우리 컴퓨터의 메모리는 크게 RAM과 하드디스크로 구성되어 있다. RAM은 메인 메모리(주 기억장치), 하드디스크는 보조 기억장치라고 불리는데, 메인 메모리는 컴퓨터가 수행하는 연산 등의 CPU가 처리 중인 데이터나 명령을 일시적으로 저장한다. 메인 메모리는 보조 기억장치(HDD, SSD 등)보다 연산 속도가 몇 배 이상 빠르다는 특징이 있다. 그러나 메인 메모리의 대표적인 단점은 가격이다. SSD와 RAM의 1GB당 가격은 RAM이 약 20~30배 정도 비싸다. 물론 둘의 역할이 엄연히 다르기 때문에 단순히 가격을 두고 비교하는 것은 큰 의미가 없지만, 이러한 점 때문에 가상 메모리라는 개념이 탄생했다고 해도 과언이 아니라고 생각한다. 우리가 현재 왜 대용량 RAM(512GB, 1TB 등)을 사용하지 않는지, 또한 사용할 필요가 없음을 이야기해보고자 한다.
초창기 컴퓨터에는 메인 메모리의 크기가 실행하고자 하는 애플리케이션보다 커야했다. CPU는 오로지 RAM과 소통을 하기 때문이다. 그러나 사실상 애플리케이션 전체를 RAM에 올려두는 것은 굉장히 힘든 일이다. 컴퓨터의 사양은 각각 다르기 때문에 모든 컴퓨터에서 돌아갈 프로그램을 만드는 것이 불가능에 가까울 뿐더러, 비용적인 측면에서도 상당한 부담이 따른다.
이를 해결하기 위해서 오버레이 기법(애플리케이션의 일부만을 RAM에 올리는 방법)을 개발하기도 하였지만 절대적인 해결책이 되지는 못했다. 결국은 시스템이 애플리케이션을 위한 충분한 공간을 가지고 있어야했기 때문이다.
용량 측면에서의 문제점 말고도 실제 메모리에서 작업을 하던 도중 발생하는 오류들에 대처하는 방식에도 위험이 따랐다. 메인 메모리에 직접 접근하여 작업을 하던 중 오류가 발생하면 해당 부분을 한동안 사용할 수 없는 문제가 생기기 때문이다. 만약 메인 메모리의 모든 부분에 오류가 발생했다면 즉시 기기가 멈추는 현상이 발생한다.
가상 메모리 기법은 애플리케이션을 실행하기 위한 필요한 최소한의 메모리가 얼마인지에 초점을 두는 기법이다. 해당 기법은 CPU가 애플리케이션을 실행하면서 명령을 수행할 때, 가상 메모리에 접근하도록 한다. 이를 위해 MMU(Memory Management Unit)를 사용한다. MMU는 가상 주소를 물리 주소로 변환하고, 잘못된 메모리 접근을 방지하여 메모리를 보호하는 역할을 수행한다. CPU가 명령을 수행하기 위해서 직접 메모리에 접근하지 못하도록 제한하고 가상 메모리에 접근하도록 하는데, 이때 MMU가 CPU와 메인 메모리 사이에서 주소를 번역한다. 그러나 모든 메모리를 번역하는 것은 상당히 비효율적이므로 MMU는 메인 메모리를 페이지(pages)라는 단위로 나누어 각 페이지를 독립적으로 처리한다. 또한 메인 메모리에는 필요한 페이지만을 올려두는데 이러한 방식을 Demand Paging(요구 페이징)이라고 한다.
아래에서 예시를 통해 설명하겠지만, 가상 메모리에서는 보조 기억장치가 메인 메모리를 캐시처럼 사용한다. 모든 데이터는 보조 기억장치에 존재하고, 필요할 때만 메인 메모리에 페이지를 올린다. 단순히 메인 메모리의 공간이 부족하다고 보조 기억장치까지 확장하여 사용하는 것이 아니라는 것에 유의해야한다.
가상 메모리를 사용함으로써 프로그램의 크기가 메인 메모리의 용량에 대해 영향을 적게 받게 되었다. 다수의 프로그램이 이미 적재되어있는 페이지를 접근하며 메모리를 효율적으로 사용하기도 할 수 있다.
또한 오류가 발생하더라도 OS에서 이를 미리 알아채고 관리할 수 있다. 가상 메모리와 실제 메모리 사이에서 OS가 연결을 관리하기 때문에 메인 메모리의 특정 부분이 죽는 경우를 방지할 수 있다.
실제 프로그램이 작동할 때 다음과 같은 방식으로 가상 메모리가 사용된다.
우선 CPU는 해당 프로그램의 명령어를 순차적으로 가져와 명령을 실행한다. 이 때 프로그램은 메모리의 실제 주소를 사용하는 것이 아니라 가상 주소(logical address)를 사용한다.
프로그램이 가상 주소를 사용하여 메모리에 접근하면, CPU는 MMU에게 가상 주소를 전달한다. MMU는 가상 주소를 페이지 테이블을 통해 실제 주소(물리 주소)로 변환한다. 페이지 테이블은 가상 주소와 실제 주소의 매핑 정보를 가지고 있는 테이블이다.
변환된 실제 주소를 기반으로 CPU는 메인 메모리에 접근한다. 이때 필요한 페이지가 메인 메모리에 있는 경우와 없는 경우로 나뉜다. Demand paging방식을 사용하기 때문에 필요한 페이지만 그때 그때 필요한 페이지만을 메인 메모리에 올려두기 때문이다. 만약 필요한 페이지가 메인 메모리에 존재한다면 그대로 사용한다.
만약 필요한 페이지가 메인 메모리에 없다면 Page fault(페이지 부재)가 발생한다. 이 상황이 발생하면, MMU가 인터럽트를 발생시킨다(Demand paging에서 트랩이라고 지칭함). 이후 운영체제가 해당 프로세스를 잠시 대기 상태로 만들고 보조 기억장치에서 페이지를 메인 메모리의 빈 공간에 올리고 페이지 테이블을 갱신한다. 갱신이 완료되면 해당 프로세스의 대기 상태를 해제하고, 해당 명령을 다시 실행한다.
메인 메모리에 페이지를 올리다보면 언젠가 메인 메모리에 빈 공간이 없는 경우가 발생한다. 이런 상황이 발생한 경우에는 적절한 페이지의 교체가 필요하다. 페이지 교체 방식에는 여러 방식이 존재한다. 대표적인 알고리즘은 다음과 같다.
여기서 알고리즘의 작동 방식에 대해서 다루지는 않겠지만, 각 알고리즘마다의 장단점이 존재하기 때문에 선택적으로 알고리즘을 사용한다.
현재 사용중인 OS들은 가상 메모리를 사용하고 있기 때문에 프로그램을 돌리기 위해서 대용량의 메인 메모리를 사용할 필요가 없다. 특정 분야가 아니라면 32GB RAM조차도 전부 사용하는 경우는 드물다. 또한 이러한 가상화 덕분에 여러 프로세스를 껐다 켜는 과정을 여러번 반복해도 OS에는 지장이 없다. 대표적으로 모바일 기기에서 여러 앱들을 껐다가 켜도 모바일 기기가 죽는 등의 문제가 초래되지 않는 것이다. 만약 가상 메모리를 사용하지 않았더라면 낮은 확률로 메인 메모리를 더 이상 사용할 수 없는 상황이 초래되어 기기가 죽는 현상이 발생할 수도 있다.