우리는 평소에 어떤 프로그램이 실행될 때 그 프로그램이 마치 온전히 메모리(RAM)에 올라가 동작한다고 생각하곤 한다. 하지만 현실은 그렇지 않다. 실제 물리 메모리는 제한되어 있고, 여러 개의 프로그램이 동시에 돌아가는 멀티태스킹 환경에서는 모든 프로그램이 메모리를 차지하기엔 공간이 턱없이 부족하다. 이러한 상황에서 운영체제가 도입한 중요한 개념이 바로 가상 메모리(Virtual Memory)다.
가상 메모리는 물리적인 한계를 뛰어넘어 각 프로세스가 자신만의 독립된 메모리 공간을 갖는 것처럼 착각하게 만든다. 이를 통해 운영체제는 실제 메모리보다 더 많은 프로그램을 동시에 실행시킬 수 있게 된다. 이 기술 덕분에 사용자는 여러 창을 띄우고도 성능 저하 없이 사용할 수 있으며 프로세스 간 간섭 없이 안전하게 메모리를 분리할 수 있다.
RAM은 크기가 정해져 있다. 예를 들어 8GB RAM이 있는 시스템에서 사용자가 브라우저, IDE, 음악 스트리밍, 메신저 등 여러 프로그램을 실행하면 실제로는 모든 프로그램을 RAM에 다 올릴 수 없다. 게다가 이들 프로그램이 동시에 실행되며 동시에 메모리를 요구하게 된다.
또한, 한 프로그램이 다른 프로그램의 메모리 공간에 접근하거나 수정한다면 보안상 치명적인 문제가 발생할 수 있다. 예를 들어 악성코드가 실행되어 다른 프로세스의 데이터를 엿볼 수 있다면 개인정보 유출 같은 문제가 생긴다. 이런 문제를 해결하기 위해 운영체제는 다음과 같은 방식으로 가상 메모리를 사용한다.
결국 가상 메모리는 "없는 메모리를 있는 것처럼 보여주는 착시"를 만들어내는 시스템이라 볼 수 있다.
개념 | 설명 |
---|---|
가상 주소 (Virtual Address) | 프로세스가 사용하는 주소. 운영체제가 프로세스에게 보여주는 논리적 주소 공간 |
물리 주소 (Physical Address) | 실제 컴퓨터의 RAM 주소. 물리적으로 존재하는 메모리 공간 |
모든 프로세스는 자기만의 가상 주소 공간을 가진다. 즉, 프로세스 A도 0x00000000
부터 시작하고 프로세스 B도 같은 주소에서 시작한다. 그런데도 충돌이 일어나지 않는 이유는 운영체제가 가상 주소를 물리 주소에 매핑해주는 중재자 역할을 하기 때문이다.
이 매핑은 프로그램이 실제 메모리에 접근하는 것이 아니라 운영체제가 “이 주소는 실제로 여기야”라고 알려주는 과정이라고 이해하면 쉽다.
가상 주소와 물리 주소는 운영체제가 하드웨어(MMU)와 데이터 구조(페이지 테이블)를 통해 연결해준다.
가상 주소 (VA) = [페이지 번호 | 오프셋]
↓
페이지 테이블 조회
↓
물리 주소 (PA) = [프레임 번호 | 오프셋]
가상 주소: 0x00401234
페이지 크기: 4KB = 2¹² = 0x1000
→ 페이지 번호 = 상위 20비트 = 0x00401
→ 오프셋 = 하위 12비트 = 0x234
페이지 테이블에서:
0x00401 → 물리 프레임 번호 0x00123
→ 물리 주소 = 0x00123000 + 0x234 = 0x00123234
이렇게 계산된 물리 주소를 통해 실제 RAM에 접근하게 된다.
운영체제는 가상 메모리와 물리 메모리를 일정한 단위로 나눠 관리한다.
가상 메모리 → 페이지(Page): 일반적으로 4KB
물리 메모리 → 프레임(Frame): 페이지와 동일한 크기
이렇게 일정한 크기로 나누면 메모리 단편화 문제를 줄이고 가상 주소와 물리 주소 간의 변환이 간편해진다. 운영체제는 페이지 단위로 메모리를 관리하며, 사용하지 않는 페이지는 디스크에 유지하고 자주 사용하는 페이지는 메모리에 유지시킨다.
[가상 주소 공간]
+---------+---------+---------+
| Page 0 | Page 1 | Page 2 |
+---------+---------+---------+
↓
[페이지 테이블]
Page 0 → Frame 9
Page 1 → Frame 4
Page 2 → Frame 2
↓
[물리 메모리]
+---------+---------+---------+
| Frame 2 | Frame 4 | Frame 9 |
+---------+---------+---------+
페이지 폴트는 어떤 가상 주소에 접근했을 때 그 주소에 해당하는 페이지가 물리 메모리에 없을 경우 발생한다.
이 과정에는 디스크 I/O가 발생하므로 수 ms~수십 ms까지 시간이 걸릴 수 있다. (RAM은 ns 단위)
스왑은 물리 메모리가 부족할 때 사용 빈도가 낮은 페이지를 디스크로 이동시켜 RAM 공간을 확보하는 기법이다. 디스크는 느리지만 공간이 크기 때문에 메모리를 가상으로 확장하는 효과가 있다.
운영체제는 스왑 파일(Swap File) 또는 스왑 파티션(Swap Partition)을 사용한다.
[RAM] ⇄ [디스크의 Swap 영역]
스래싱은 운영체제가 너무 자주 스왑 인/아웃을 반복해서 성능이 급격히 저하되는 현상이다.
해결 방법: RAM 증설, 실행 중인 프로세스 수 제한, 메모리 관리 정책 개선(LRU 등)
이번에 가상 메모리를 정리하면서 운영체제가 실제 메모리를 어떻게 다루는지를 조금 더 실감나게 이해할 수 있었다. 그동안 프로그램을 실행하면 무조건 RAM에 올라가 있다고만 생각했는데 실제로는 그 프로그램의 일부분만이 메모리에 올라가고 나머지는 필요할 때만 로딩된다는 점이 흥미로웠다.
가상 메모리를 통해 운영체제는 RAM의 용량 제약을 뛰어넘어 더 많은 프로그램을 실행할 수 있게 하고 동시에 프로세스 간의 메모리 접근을 철저히 분리해 보안성을 확보한다는 점이 인상 깊었다. 특히 MMU와 페이지 테이블을 통한 주소 변환, 페이지 폴트와 스왑 처리 과정은 실제 컴퓨터 내부에서 일어나는 복잡한 일들을 아주 정교하게 제어하는 원리를 보여준다. 시스템 병목이나 성능 저하 이슈를 겪는다면 이 가상 메모리의 동작을 이해하는 것이 많은 힌트를 줄 수 있을 것 같다.
참고