본문으로 건너뛰기

가상 메모리

가상 메모리는 물리 메모리의 크기와 상관없이 메모리를 이용할 수 있도록 지원하는 기술이다. 프로그래머는 가상 메모리 덕분에 물리 메모리의 크기에 구애받지 않고 작업할 수 있는 커다란 공간을 얻게 되는 셈이다.

즉, 가상 메모리는 크기가 다른 물리 메모리에서 일관되게 프로세스를 실행할 수 있는 기술이다.

다중 프로그래밍을 실현하기 위해서는 많은 프로세스들을 동시에 메모리에 올려두어야 한다. 가상 메모리는 프로세스 전체가 메모리 내에 올라오지 않더라도 실행이 가능하도록 하는 기법이며, 프로그램이 물리 메모리보다 커도 된다는 주요 장점이 있다.

가상 메모리라는 것은 단어 그대로의 Virtual이 아니다. 마치 없는 것을 만들어내는 듯한 느낌인데, 실제로는 이미 있는 것을 다르게 사용하는 의미히다. 가상 메모리의 실제 공간은 하드디스크 + RAM이다.

물론 실제로 프로세스가 실행될 수 있는 메모리 공간은 램 크기 만큼이지만 운영체제가 가상 메모리 공간을 프로세스에게 제공해줌으로써 프로세스는 자신을 모두 메모리에 적재했다는 착각을 한다. 프로세스 입장에서는 가상 메모리 공간도 물리 메모리 공간으로 인식하는 것이다. 그리고 운영체제는 가상 메모리에 걸쳐서 적재된 프로세스의 부분이 실제로 적재가 필요하다면 물리 메모리 공간과 swap 해준다.

프로세스의 모든 코드가 한번에 물리 메모리에서 돌아가는 일은 없다. 필요한 부분만 실제 물리 메모리로 올라가기만 하면 되기 때문에, 이 부분을 운영체제가 계속 swapping 해주는 것을 통해 가상 메모리를 구현한다. 애플리케이션이 실행될 때, 실행에 필요한 일부분만 메모리에 올라가며 애플리케이션의 나머지는 디스크에 남게된다. 즉, 디스크가 RAM의 보조 기억장치(backing store)처럼 작동하는 것이다. 가상 메모리를 구현하기 위해서는 컴퓨터가 특수 메모리 관리 하드웨어를 갖추고 있어야 한다. (MMU, Memory Management Unit)

MMU는 가상 주소를 물리주소로 변환하고, 메모리를 보호하는 기능을 수행한다. MMU를 사용하게 되면, CPU가 각 메모리에 접근하기 이전에 메모리 주소 번역 작업이 수행된다. 그러나 메모리를 일일이 가상 주소에서 물리적 주소로 번역하게 되면 작업 부하가 너무 높아지므로, MMU는 RAM을 여러부분(페이지, pages)로 나누어 각 페이지를 하나의 독립된 항목으로 처리한다. 페이지 및 주소 번역 정보를 기억하는 작업이 가상 메모리를 구현하는데 있어 결정적인 절차이다.

가상 메모리 등장 배경

초창기 컴퓨터에서는 사용 가능한 RAM 용량이 가장 큰 실행 애플리케이션의 주소 공간보다 커야했다. 그렇지 않을 경우 메모리 부족 오류로 인해 해당 애플리케이션을 실행할 수 없다.

이후 컴퓨터에서는 프로그래머가 애플리케이션의 일부만 기억장치에 올려 실행할 수 있도록 지정할 수 있게 하는 오버레이 기법을 사용하여 메모리 부족 문제를 해결하려고 했다. 하지만 이 역시 전반적인 메모리 부족 문제를 해결할 수 없었다. 오버레이를 사용하는 프로그램은 그렇지 않은 프로그램보다는 메모리를 덜 사용했지만, 애초에 시스템이 프로그램을 위한 충분한 메모리를 갖추고 있지 않은 경우 똑같은 메모리 부족 오류가 발생했다.

여기에서 더 발전한 가상 메모리 기법은 애플리케이션을 실행하는데 얼마나 많은 메모리가 필요한지에 집중하지 않고, 대신 애플리케이션을 실행하는데 최소한 만큼의 메모리가 필요한가에 집중하여 문제를 해결하고자 했다.

이러한 접근 방식이 가능한 이유는 메모리 접근은 순차적이고 지역화되어 있기 때문이다. 애플리케이션의 일부만 메모리에 올리고 메모리에 올라가지 않는 나머지는 보조 기억장치, 디스크에 위치시킨다. 가상 메모리의 핵심은 보조 기억장치이다.

  • 물리 메모리 크기에 제약을 받지 않게 된다.
  • 더 많은 프로그램을 동시에 실행할 수 있게 된다. 이에 따라 응답시간은 유지되고, CPU 이용률과 처리율은 높아진다.
  • 스왑에 필요한 입출력이 줄어들기 때문에 프로그램들이 빠르게 실행된다.

스왑

32bit CPU의 최대 메모리 크기는 4GB이다. 이 시스템에서 각각 4GB 주소 공간을 차지하는 10개의 프로세스를 동시에 실행하려면 운영체제를 포함하여 적어도 40GB의 메모리가 필요하다. 이 경우 가상 메모리 시스템에서는 물리 메모리의 내용 중 일부를 하드디스크의 일부 공간, 즉 스왑 영역으로 옮긴다. 스왑 영역은 하드디스크에 존재하지만 메모리 관리자가 관리하는 영역으로서 메모리의 일부이며, 스왑 메모리의 구성 요소 중 하나이다. 메모리 관리자는 물리 메모리의 부족한 부분을 스왑 영역으로 보충한다. 즉 물리 메모리가 꽉 찼을 때 일부 프로세스를 스왑 영역으로 보내고(스왑아웃), 몇 개의 프로세스가 작업을 마치면 스왑 영역에 있는 프로세스를 메모리로 가져온다.(스왑인)

가상 메모리에서 메모리 관리자가 사용할 수 있는 메모리의 전체 크기는 물리메모리(실제 메모리)와 스왑 영역을 합한 크기이다.

가상 메모리 시스템에서 메모리 관리자는 물리 메모리와 스왑 영역을 합쳐서 프로세스가 사용하는 가상 주소를 실제 물리 주소로 변환하는데 이러한 작업을 동적 주소 변환(Dynamic Address Translation, DAT)라고 한다. 동적 주소 변환을 거치면 프로세스가 아무 제약 없이 사용자의 데이터를 물리 메모리에 배치할 수 있다.

논리주소와 물리주소

메모리에는 고유의 주소값이 있다. 이를 물리 주소라고 부른다. 가상 메모리는 하드디스크 공간을 이용해 마치 물리메모리가 훨씬 커진 것처럼 사용한다. 그러면 그 커진 메모리에도 주소가 붙어야 하는데, 물리 메모리가 아니기 때문에 물리 주소가 붙을 수 없다. 이를 위해 붙이는 주소가 논리 주소이다. 물리 메모리에는 일반 프로세스 뿐만 아니라 운영체제도 적재되어 사용되고 있다. 이 운영체제가 사용중인 메모리는 직접 접근하면 안되는 메모리 공간이다. 때문에 운영체제 부분의 다음 주소부터 논리 주소 0번으로 시작한다. 그리고 가상 메모리까지 모든 주소를 붙이게 되는 것이다.

그렇다면 논리 주소만으로 메모리 접근이 가능한가? 실제로 데이터 처리를 하기 위해서는 메모리에 주소를 통해 접근해야 한다. 논리 주소만으로는 메모리에 접근할 수 없다. 때문에 이 논리주소를 물리 주소로 변환시켜주는 과정이 필요하다.

예를 들어 현재 하드디스크 공간(가상메모리)부분에서 적재되었다고 판단되는 프로세스 데이터가 실제로 물리메모리에 올라와야될 경우(실행 되어야 할 경우) 논리 주소를 물리 주소에 연결한다. 이를 매핑이라고 한다. 이 매핑을 관리하는 것이 메모리 관리자의 역할이다.

메모리 관리자

위에서 프로세스를 필요한 부분만 물리메모리에 올린다고 했다. 그러면 당연히 프로세스를 어떻게 쪼갤것인지, 물리 메모리에는 어떻게 적재할 것인지, 만약 물리메모리가 꽉 차면 어떻게 하는지 궁금할 것이다. 이 부분들을 담당하는 것이 메모리 관리자이다.

  • 가져오기(fetch): 프로세스가 필요로 하는 데이터를 언제 메모리로 가져올지 결정하는 정책이다. 프로세스가 해당 코드 데이터가 필요할 경우 불러오는 것이 일반적이지만, 조금 더 빠르게 이 데이터를 받아오기 위해 캐싱을 사용한다.
  • 배치: 가져온 프로세스 조각을 메모리의 어떤 곳에 위치 시킬지 결정하는 정책이다. 프로세스 조각의 크기는 천차만별일 것이다. 메모리를 일정한 크기로 잘라놓고 그 크기에 프로세스를 맞게 자르는 기법을 페이징이라고 하고, 프로세스를 자르고 거기에 메모리 크기를 맞추는 것을 세크먼테이션(segmentation)이라고 한다.
  • 재배치: 메모리가 꽉 찼을 때 어떤 프로세스를 내보낼지 결정한다. 사용하지 않을 프로세스만 내보내면 가장 효율적이겠지만 그런 프로세스를 찾기 힘들고 가장 가깝게 효율적인 알고리즘을 도입하도록 해야한다.

메모리 분할 방식(배치)

메모리 분할 방식은 효율성을 추구하기 위한 운영체제의 끝 없는 싸움이다. 페이징과 세그먼테이션 두 방식 모두가 장단점이 존재한다. 두 가지 방식을 적절히 섞어야 효율적인 메모리 운용이 가능하다.

페이징

페이징은 가상 메모리를 일정 크기로 쪼개서 그 크기에 맞춰 프로세스를 잘라 넣는 방식이다. 가상 메모리를 물리 메모리에서 사용하기 위해서는 반드시 주소 변환이 필요하다. 페이징은 가상메모리를 쪼개서 만들고 물리 메모리도 페이지와 같은 크기로 쪼개서 "프레임"으로 만든다. 그리고 해당 페이지와 프레임을 매핑한 페이지 테이블을 만든다. 이 페이지 테이블에는 인덱스(페이지 번호)가 붙은 공간에 프레임 번호가 들어가있다. 그리고 페이지는 반드시 프레임보다 개수가 많을 수 밖에 없기 때문에 invalid 값이 들어간 경우가 있다. 이 경우는 해당 페이지가 스왑 영역(하드디스크 부분)에 존재한다는 이야기이다.

페이징의 장점은 메모리 조각의 사이즈가 모두 같기 때문에 해당 메모리를 차곡차곡 쌓고 많약 이 메모리가 해제된다고 하더라도, 바르 다른 데이터를 그 자리에 바로 집어넣을 수 있다.(모두 다 같은 크기이므로)

하지만 프로세스 크기가 작더라도 같은 메모리 사이즈로 쪼개야 하기 때문에 해당 블록에 메모리가 남을 수 있다. 이것을 내부 단편화라고 한다. 이런 현상이 심해지면 메모리 낭비가 심해질 것이다.

세그먼테이션

페이징 기법이 물리 메모리와 가상 메모리 공간을 같은 크기로 쪼개서 프로세스를 끼워넣는 방식이었다면 세그먼테이션은 프로세스 크기에 맞춰 물리 메모리와 가상 메모리를 쪼개는 방식이다. 물리 메모리에는 프로세스 크기에 맞춰 물리메모리와 가상 메모리를 쪼개는 방식이다. 물리 메모리에는 프로세스 크기에 맞춰 세그먼트가 배치되게 된다. 세그먼테이션 기법에서는 같은 단위 크기의 메모리를 사용하지 않기 때문에 반드시 시작 메모리 주소가 매핑 테이블에 포함되어야 한다. 페이징의 경우 모든 페이지의 크기가 같았기 때문에 따로 기록할 필요가 없었다.

세그먼테이선의 장점은 내부 단편화가 전혀 생기지 않고 페이지 테이블이 단순하다는 것이지만, 물리 메모리의 메모리 구조가 복잡해진다는 단점이 있다. 왜냐하면 프로세스 크기에 맞춰 물리 메모리에 계쏙 적재되고 삭제하는 것이 반복되면서 큰 프로세스는 메모리 사이에 들어가지 못하기 때문이다. 이를 외부 단편화라 하는데 프로세스 처리가 반복되면 물리 메모리에 구멍이 계속 나게 된다.

혼용 기법

세그먼테이션과 페이징 기법을 혼용하는 것은 프로세스를 세그먼트 단위로 쪼개고 이것을 다시 페이지 단위로 쪼개는 것을 의미한다.

이 방식의 장점은 프로세스를 바로 페이징해버리면 프로세스의 코드, 스택, 힙 데이터 영역 등이 실제 논리관계랑 상관없이 물리크기로 잘려버리기 때문에 코드의 안정성이 떨어진다는 단점을 해결할 수 있다는 것이다.

Demand Paging(요구 페이징)

프로그램 실행 시작시에 프로그램 전체를 디스크에서 물리 메모리에 적재하는 대신, 초기에 필요한 것들만 적재하는 전략을 요구 페이징이라고 하며, 가상 메모리 시스템에서 많이 사용된다. 그리고 가상 메모리는 대개 페이지로 관리된다. 요구 페이징을 사용하는 가상 메모리에서는 실행 과정에서 필요해질 때 페이지들이 적재된다. 한 번도 접근되지 않은 페이지는 물리 메모리에 적재되지 않는다.

페이지 폴트

페이지 폴트란 어떤 페이지에 접근하려고 했을 때 해당 페이지가 실제 물리 메모리에 부재할 때 발생하는 인터럽트이며, 페이지 폴트가 발생하면 운영체제가 이를 해결한 뒤 다시 동일한 명령을 수행하는 식으로 동작한다.

  • 페이지 폴트란, 어떤 프로그램이 자신의 주소공간(가상 메모리 공간)에는 존하지만 시스템의 RAM에는 현재 존재하지 않는 데이터, 코드에 접근을 시도할 경우 발생하는 현상을 의미한다.
  • 페이지 폴트가 발생하면 운영체제는 그 데이터를 메모리로 가져와서, 마치 페이지 폴트가 전혀 발생하지 않은 것처럼 프로그램이 계속적으로 동작하게 해준다.
  • 이러한 페이지 폴트가 자주 일어날수록 운영체제의 성능이 많이 저하되기 때문에 페이지 폴트가 일어나지 않도록 하는 것이 중요하다. 페이지 폴트를 최소화하기 위한 방법으로는 페이지 교체 정책(page replacement policy)이 있다.
    • 메모리가 꽉 차있을 때 기존 페이지 중 하나를 물리메모리에서 저장 매체로 내리고, 새로운 페이지를 방금 비워진 해당 물리 메모리 공간에 올린다. 이때 기존 페이지 중 어떤 것을 내리면 좋을지에 대한 알고리즘을 짠 것이 바로 페이지 교체 알고리즘이다.

TLB(Translation Lookaside Buffer)

TLB는 가상 메모리 주소를 물리적 주소로 변환하는 속도를 높이기 위해 사용하는 캐시로, 최근에 일어난 가상 메모리와 물리 주소의 변환을 테이블을 저장해둔다. CPU가 가상 주소를 가지고 메모리에 접근하려고 할 때 우선은 TLB에 접근하여 가상 주소에 해당되는 물리 주소를 찾고, 만약 TLB에 매핑이 존재하지 않는다면 MMU가 페이지 테이블에서 해당되는 물리 주소로 변환한 후 메모리에 접근하게 된다.

TLB는 MMU에 포함되어 있는 작은 캐시로, 일종의 주소 변환 캐시이다. 캐시 메모리란, 속도가 빠른 저장장치와 느린 저장장치 사이에서 속도 차에 따른 병목 현상을 줄이기 위한 범용 메모리를 의미한다. 예를 들어 CPU에서의 캐시 메모리는 CPU 코어(고속)와 메모리(CPU에 비해 저속) 사이에서 속도 차에 따른 병목 현상을 완화하는 역할을 한다.

Reference