이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.14 21:43
가상 메모리는 컴퓨터 시스템이 실제 물리 메모리보다 더 큰 용량의 메모리 공간을 사용할 수 있도록 하는 메모리 관리 기술이다. 이 기술은 운영체제와 하드웨어(주로 메모리 관리 장치)가 협력하여 구현하며, 각 프로세스에게 독립적이고 연속적인 메모리 공간인 가상 주소 공간을 제공한다. 사용자 프로그램은 이 가상 주소를 통해 메모리에 접근하고, 시스템은 이를 실제 물리 주소로 변환하여 실행한다.
가상 메모리의 핵심 아이디어는 프로그램의 전체 코드와 데이터를 물리 메모리에 상주시킬 필요가 없다는 점이다. 대신, 필요한 부분만 메모리에 올리고, 당장 필요하지 않은 부분은 보조기억장치(예: HDD, SSD)의 특별한 영역(예: 스왑 영역)에 보관한다. 프로그램이 보조기억장치에 있는 데이터에 접근하려 하면, 시스템은 해당 데이터를 물리 메모리로 불러오고, 필요하다면 다른 데이터를 보조기억장치로 내보낸다. 이 과정을 통해 제한된 물리 메모리 자원을 효율적으로 공유하고 관리할 수 있다.
이 기술은 1960년대 초반 맨체스터 대학교의 아틀라스 프로젝트에서 처음 구현된 것으로 알려져 있다[1]. 이후 현대의 대부분의 범용 운영체제(예: 윈도우, 리눅스, macOS)와 프로세서 아키텍처(예: x86, ARM)에서 표준 기능으로 채택되었다.
가상 메모리는 다음과 같은 주요 이점을 제공한다.
* 메모리 확장: 실제 물리 메모리 크기를 넘어서는 대용량 프로그램의 실행을 가능하게 한다.
* 메모리 추상화: 각 프로세스에게 통일되고 독립된 메모리 공간을 제공하여 프로그래밍을 단순화한다.
* 메모리 보호: 한 프로세스의 메모리 공간이 다른 프로세스나 운영체제 커널의 공간을 침범하지 못하도록 격리한다.
* 공유 메모리: 여러 프로세스가 동일한 코드나 데이터(예: 라이브러리)를 물리 메모리의 한 복사본으로 공유할 수 있게 한다.
가상 메모리는 운영체제가 제공하는 핵심 기능 중 하나로, 각 프로세스에게 독립적이고 연속적인 메모리 공간의 환상을 제공한다. 이를 통해 애플리케이션은 실제 물리 메모리의 제약이나 배치 상황을 신경 쓰지 않고 프로그래밍할 수 있다. 이 개념의 핵심은 가상 주소 공간과 물리 주소 공간의 분리, 그리고 이 둘 사이의 변환을 담당하는 메모리 관리 장치(MMU)에 있다.
프로세스가 접근하는 모든 메모리 주소는 가상 주소이다. 이 가상 주소의 집합이 가상 주소 공간을 구성한다. 일반적으로 각 프로세스는 자신만의 독립된 가상 주소 공간을 가지며, 이 공간은 운영체제에 의해 0번지부터 시작하는 연속적인 공간으로 인식된다. 반면, 물리 주소 공간은 실제 RAM 하드웨어의 메모리 셀 주소 체계를 가리킨다. 운영체제와 메모리 관리 장치(MMU)는 프로세스의 가상 주소를 적절한 물리 주소로 변환하는 작업을 투명하게 수행한다.
이 변환 과정의 핵심 장치는 메모리 관리 장치(MMU)이다. CPU가 메모리에 접근할 때마다 생성하는 가상 주소는 MMU로 입력된다. MMU는 내부의 변환 로직(주로 페이지 테이블을 참조)을 사용해 해당 가상 주소를 실제 데이터가 저장된 물리 주소로 변환한다. 이 변환이 완료되어야만 실제 물리 메모리 버스에 접근이 이루어진다. MMU의 존재로 인해 프로그램은 물리 메모리의 실제 배치나 다른 프로세스의 메모리 사용 상황을 전혀 알 필요가 없어진다.
가상 주소 공간과 물리 주소 공간의 분리는 여러 중요한 이점을 제공한다. 첫째, 메모리 보호가 가능해진다. 각 프로세스의 가상 주소 공간은 고유하므로, 한 프로세스가 다른 프로세스의 메모리 영역을 함부로 접근할 수 없다. 둘째, 물리 메모리의 단편화를 프로그램에게 숨길 수 있다. 프로그램이 필요로 하는 데이터가 물리 메모리 내에서 흩어져 저장되어 있더라도, 가상 주소 공간에서는 연속적으로 보이게 할 수 있다. 이 모든 것은 MMU에 의한 주소 변환 메커니즘을 기반으로 구현된다.
가상 메모리 시스템에서 프로세스가 접근하는 메모리 주소는 가상 주소이다. 이 가상 주소는 CPU에 의해 생성되며, 물리 메모리의 실제 위치를 직접 가리키지 않는다. 각 프로세스는 독립적인 가상 주소 공간을 할당받으며, 일반적으로 0부터 시작하는 연속적인 주소 범위로 구성된다. 이 공간은 코드, 데이터, 힙, 스택과 같은 논리적인 영역으로 나뉜다.
물리 주소 공간은 시스템에 설치된 실제 RAM 하드웨어의 주소 공간을 의미한다. 이 공간은 모든 프로세스와 운영체제 커널이 공유한다. 가상 주소 공간의 주소는 메모리 관리 장치를 통해 물리 주소로 변환된다. 이 변환 과정은 페이지 테이블이라는 자료 구조를 통해 관리된다.
가상 주소 공간의 크기는 물리 메모리의 크기와 무관하게 설정될 수 있다. 예를 들어, 32비트 시스템은 4GB(2^32)의 가상 주소 공간을 제공하며, 이는 실제 물리 메모리가 1GB밖에 없더라도 가능하다. 이는 사용되지 않는 부분의 데이터를 보조기억장치로 옮겨서 관리하는 방식으로 구현된다. 반면, 물리 주소 공간의 크기는 시스템에 장착된 RAM 칩의 용량에 의해 제한된다.
특성 | 가상 주소 공간 | 물리 주소 공간 |
|---|---|---|
주체 | 각 프로세스별로 독립적 존재 | 시스템 전체에서 하나만 존재 |
크기 | 주소 체계에 의해 이론적으로 결정 (예: 4GB) | 설치된 RAM 하드웨어 용량에 의해 결정 |
연속성 | 프로세스에게 연속적으로 보임 | 실제로는 페이지 프레임 단위로 조각날 수 있음 |
접근 | 프로세스가 직접 사용하는 주소 | MMU 변환 후 실제 메모리에 접근할 때 사용되는 주소 |
이러한 분리는 여러 중요한 이점을 제공한다. 첫째, 프로세스는 다른 프로세스의 물리 메모리 영역을 알지 못하므로 메모리 보호가 이루어진다. 둘째, 프로그래머는 물리 메모리의 제약이나 배치를 고려하지 않고 프로그램을 작성할 수 있다. 셋째, 물리 메모리보다 큰 프로그램을 실행할 수 있는 메모리 가상화의 기초가 된다.
메모리 관리 장치(MMU)는 가상 주소 공간과 물리 주소 공간 사이의 변환을 담당하는 하드웨어 장치이다. 중앙 처리 장치(CPU)가 생성하는 모든 가상 주소(논리 주소)는 MMU를 통해 실제 데이터가 저장된 물리 주소로 변환된 후에야 메모리에 접근할 수 있다. 이 변환 과정은 페이지 테이블이라는 자료 구조를 참조하여 수행된다. MMU는 운영체제가 설정한 페이지 테이블의 위치를 알고 있으며, 각 가상 주소를 해당하는 페이지 프레임의 물리 주소로 매핑한다.
MMU의 핵심 기능 중 하나는 메모리 보호를 제공하는 것이다. 운영체제는 페이지 테이블의 각 항목에 읽기, 쓰기, 실행 권한과 같은 접근 권한 정보를 설정할 수 있다. MMU는 모든 메모리 접근 시 이 권한을 검사하여, 프로세스가 허가되지 않은 메모리 영역(예: 다른 프로세스의 메모리나 운영체제 커널 영역)에 접근하려고 하면 예외(일반적으로 페이지 폴트와 구분되는 보호 오류)를 발생시킨다. 이를 통해 하나의 프로세스가 시스템 전체의 안정성을 해치는 것을 방지한다.
또한 MMU는 변환 색인 버퍼(TLB)라는 고속 캐시를 내장하고 있는 경우가 많다. TLB는 최근에 사용된 가상 주소에서 물리 주소로의 변환 결과를 저장하여, 매번 페이지 테이블을 참조해야 하는 오버헤드를 줄인다. 가상 주소가 TLB에 존재하면(TLB 히트) 변환이 즉시 이루어지지만, 존재하지 않으면(TLB 미스) 메인 메모리에 있는 페이지 테이블을 조회해야 한다. MMU는 TLB 미스 발생 시 필요한 페이지 테이블 항목을 자동으로 TLB에 적재하는 하드웨어 방식을 지원하기도 한다.
페이징 시스템은 가상 메모리를 구현하는 가장 일반적인 방식이다. 이 시스템은 프로세스의 가상 주소 공간을 고정 크기의 블록인 페이지로 나누고, 물리 메모리 역시 같은 크기의 페이지 프레임으로 나누어 관리한다. 각 페이지는 필요할 때 페이지 프레임에 적재되거나, 보조기억장치(예: HDD나 SSD)의 특별한 영역인 스왑 영역으로 내보내질 수 있다. 이 방식은 메모리를 연속적으로 할당할 필요를 없애고, 외부 단편화 문제를 효과적으로 해결한다.
페이지의 위치 변환은 페이지 테이블이라는 자료 구조를 통해 이루어진다. 각 프로세스는 자신의 페이지 테이블을 가지며, 이 테이블은 가상 페이지 번호를 해당 페이지가 상주하는 물리 페이지 프레임 번호로 매핑한다. 페이지 테이블 항목(PTE)에는 프레임 번호 외에도 페이지가 물리 메모리에 존재하는지 나타내는 유효 비트(또는 존재 비트), 읽기/쓰기 권한을 제어하는 보호 비트, 페이지가 변경되었는지 추적하는 더티 비트 등의 정보가 포함된다.
페이지 테이블은 메모리에 상주하므로, 매번 메모리 접근 시 추가로 페이지 테이블을 조회해야 하는 오버헤드가 발생한다. 이 성능 저하를 완화하기 위해 변환 색인 버퍼(TLB)라는 특수한 고속 캐시가 사용된다. TLB는 최근에 사용된 페이지 번호 변환 정보를 저장하여, 대부분의 변환 요청을 메인 메모리 접근 없이 매우 빠르게 처리할 수 있게 한다. TLB 미스가 발생한 경우에만 페이지 테이블을 메모리에서 조회하고, 그 결과를 TLB에 갱신한다.
페이지 테이블의 구조는 시스템에 따라 다양하다. 간단한 단일 수준 페이지 테이블은 구현이 쉽지만, 큰 주소 공간을 지원할 때 테이블 자체가 차지하는 메모리 공간이 매우 커질 수 있다. 이를 해결하기 위해 다중 수준(계층적) 페이지 테이블이 널리 사용된다. 예를 들어, 2단계 페이지 테이블은 페이지 테이블을 다시 페이지 크기로 나누어, 실제로 사용되는 주소 공간 영역에 대해서만 페이지 테이블 페이지를 할당하는 방식으로 메모리를 절약한다[2].
가상 메모리 시스템에서 페이징은 물리 메모리를 고정 크기의 블록으로 나누어 관리하는 기법이다. 이때 사용되는 두 가지 핵심 단위가 페이지와 페이지 프레임이다.
페이지는 프로세스의 가상 주소 공간을 일정한 크기로 나눈 논리적인 블록이다. 예를 들어 4KB, 2MB, 1GB 등의 크기를 가진다. 각 프로세스는 자신만의 독립적인 페이지 집합을 가지며, 이 페이지들은 연속적으로 할당된 것처럼 보이지만 실제 물리 메모리에서는 그렇지 않을 수 있다. 반면 페이지 프레임은 물리 메모리(RAM)를 동일한 크기로 나눈 물리적인 블록이다. 페이지의 크기와 페이지 프레임의 크기는 항상 동일하다. 운영체제는 물리 메모리를 관리할 때 페이지 프레임을 단위로 사용하며, 사용 가능한 프레임들의 목록(프리 리스트)을 유지한다.
프로세스가 특정 가상 주소에 접근하면 메모리 관리 장치(MMU)는 해당 주소가 속한 가상 페이지를 확인한다. 그 후 페이지 테이블을 조회하여 해당 가상 페이지가 매핑된 물리 페이지 프레임의 위치를 찾아낸다. 만약 해당 페이지가 현재 물리 메모리에 존재하지 않으면(페이지 폴트), 운영체제는 디스크의 스왑 영역에서 필요한 페이지를 불러와 빈 페이지 프레임에 적재한다. 이 매핑 관계는 페이지 테이블에 기록된다. 페이지와 페이지 프레임의 관계는 다음 표로 요약할 수 있다.
구분 | 설명 | 특성 |
|---|---|---|
페이지 | 가상 주소 공간의 고정 크기 블록 | 논리적 단위, 프로세스별로 독립적 |
페이지 프레임 | 물리 메모리의 고정 크기 블록 | 물리적 단위, 모든 프로세스가 공유 |
이러한 분리는 메모리 관리의 효율성과 유연성을 제공한다. 운영체제는 서로 다른 프로세스의 페이지들을 물리 메모리 내 어느 프레임에든 배치할 수 있으며, 이를 통해 단편화를 줄이고 물리 메모리를 효과적으로 활용할 수 있다. 또한, 한 프로세스의 페이지를 디스크로 스왑 아웃하고 그 프레임을 다른 프로세스의 페이지에 재할당하는 동적 메모리 관리가 가능해진다.
페이지 테이블은 가상 주소를 물리 주소로 변환하기 위한 핵심 자료 구조이다. 각 프로세스는 자신만의 페이지 테이블을 가지며, 운영체제가 이를 관리한다. 페이지 테이블의 각 항목(PTE)은 해당 가상 페이지가 매핑된 물리 페이지 프레임의 시작 주소와 함께 여러 제어 비트를 포함한다. 주요 제어 비트로는 페이지의 존재 여부(Present bit), 읽기/쓰기 권한(Read/Write bit), 사용자/커널 모드 접근 권한(User/Supervisor bit) 등이 있다.
기본적인 선형 페이지 테이블은 간단한 구조이지만, 큰 가상 주소 공간을 지원할 경우 테이블 자체의 크기가 방대해지는 문제가 있다. 예를 들어, 32비트 주소 공간과 4KB 페이지 크기를 가정하면, 약 100만 개의 항목이 필요하다. 이를 해결하기 위해 다단계 페이지 테이블 구조가 널리 사용된다. 다단계 페이지 테이블은 트리 구조를 형성하여, 실제로 사용되지 않는 주소 공간 영역에 대한 중간 단계의 페이지 테이블을 생성하지 않음으로써 공간 효율성을 높인다.
구조 유형 | 설명 | 주요 장점 | 주요 단점 |
|---|---|---|---|
단일 단계 페이지 테이블 | 모든 매핑 정보를 하나의 큰 테이블에 저장한다. | 변환이 빠르고 구현이 단순하다. | 메모리 공간을 많이 차지한다. |
다단계 페이지 테이블 (예: 2단계) | 페이지 테이블을 여러 단계(예: 페이지 디렉토리, 페이지 테이블)로 나눈다. | 사용되지 않는 영역에 대한 테이블 공간을 절약할 수 있다. | 주소 변환에 더 많은 메모리 접근이 필요하다. |
역방향 페이지 테이블 | 물리 프레임 번호를 키로 하여 가상 주소를 매핑한다. | 시스템 전체에 하나의 테이블만 존재하여 공간 효율적이다. | 가상 주소로 검색하는 것이 복잡하고 느릴 수 있다. |
페이지 테이블은 일반적으로 물리 메모리에 상주하므로, 각 가상 주소 변환 시에는 페이지 테이블에 접근하기 위해 추가적인 메모리 읽기가 필요하다. 이 오버헤드를 줄이기 위해 TLB가 사용된다. 또한, 매우 큰 주소 공간(예: 64비트)을 지원하는 시스템에서는 4단계 또는 5단계 페이지 테이블과 같은 더 복잡한 다단계 구조가 요구되기도 한다[3].
TLB(Translation Lookaside Buffer)는 가상 메모리 시스템에서 가상 주소를 물리 주소로 변환하는 속도를 높이기 위해 사용하는 고속 캐시이다. 메모리 관리 장치(MMU) 내부에 위치하며, 최근에 사용된 가상 주소와 물리 주소의 매핑 정보를 저장한다. 페이지 테이블은 주 메모리에 상주하기 때문에 매번 주소 변환을 위해 메모리에 접근하면 성능 저하가 발생한다. TLB는 이러한 변환 정보를 캐싱하여 평균적인 주소 변환 속도를 크게 향상시킨다.
TLB의 동작은 다음과 같다. CPU가 가상 주소를 생성하면, MMU는 먼저 TLB를 검색한다. 해당 가상 페이지 번호에 대한 변환 항목이 TLB에 존재하면 이를 'TLB 히트'라고 하며, 저장된 물리 프레임 번호를 즉시 사용한다. 항목이 존재하지 않으면 'TLB 미스'가 발생하며, 이때 MMU는 주 메모리에 있는 페이지 테이블을 검색하여 필요한 변환 정보를 가져온다. 이 정보는 이후 참조를 위해 TLB에 로드된다. TLB는 용량이 제한되어 있으므로, 새로운 항목을 저장할 공간이 필요할 때는 기존 항목을 교체해야 한다. 이때 사용되는 교체 알고리즘으로는 LRU(최근 최소 사용) 등이 일반적이다.
TLB의 설계는 성능에 중요한 영향을 미친다. 주요 특성은 다음과 같이 표로 정리할 수 있다.
특성 | 설명 |
|---|---|
구조 | 일반적으로 완전 연관 또는 집합 연관 매핑 방식을 사용한다. |
캐시 내용 | |
플러시 | 문맥 교환(Context Switch)이 발생하면, 새 프로세스의 주소 공간에 맞게 TLB 항목을 무효화하거나 태그를 갱신해야 한다[4]. |
성능 영향 | 높은 히트율은 메모리 접근 지연 시간을 줄여 전체 시스템 성능을 향상시킨다. |
대부분의 현대 프로세서는 계층적 TLB 구조를 채택하고 있다. 예를 들어, 작고 빠른 L1 TLB와 더 크지만 상대적으로 느린 L2 TLB를 두는 방식이다. 이는 히트율과 접근 속도 사이의 균형을 최적화하기 위한 것이다.
세그멘테이션 시스템은 가상 메모리를 구현하는 또 다른 주요 기법으로, 프로그램의 논리적 구조에 따라 메모리를 가변 크기의 블록인 세그먼트로 나누어 관리한다. 페이징 시스템이 고정 크기의 페이지를 사용하는 것과 달리, 세그멘테이션은 코드, 데이터, 스택, 힙 등 프로그램의 각 기능적 단위를 별도의 세그먼트로 할당한다. 각 세그먼트는 독립된 논리적 주소 공간을 가지며, 일반적으로 세그먼트 테이블을 통해 관리된다.
시스템은 각 세그먼트에 대해 베이스(시작 물리 주소)와 리밋(세그먼트 길이) 정보를 가진 세그먼트 디스크립터를 유지한다. CPU가 논리 주소를 생성할 때, 주소는 일반적으로 세그먼트 번호와 세그먼트 내 오프셋으로 구성된다. 메모리 관리 장치(MMU)는 세그먼트 번호를 인덱스로 세그먼트 테이블을 참조하여 베이스 주소를 얻고, 여기에 오프셋을 더해 물리 주소로 변환한다. 이 과정에서 오프셋이 리밋 값을 초과하는지 검사하여 메모리 보호를 제공한다.
세그멘테이션의 주요 장점은 프로그래머의 관점에서 메모리를 자연스럽게 인식할 수 있게 하며, 의미 단위로 접근 권한 설정과 공유가 용이하다는 점이다. 예를 들어, 코드 세그먼트는 읽기-실행만 허용하고, 데이터 세그먼트는 읽기-쓰기를 허용하는 방식으로 보안을 강화할 수 있다. 그러나 가변 크기 할당으로 인해 외부 단편화가 발생하기 쉽다는 단점이 있다. 이는 물리 메모리에 사용 가능한 공간이 충분함에도 불구하고, 연속된 공간이 부족해 세그먼트를 할당하지 못하는 상황을 초래한다.
현대 운영체제는 순수한 세그멘테이션보다는 페이징과 세그멘테이션을 결합한 세그먼트-페이징 혼용 방식을 주로 사용한다. 이는 인텔 x86 아키텍처의 보호 모드에서 볼 수 있는데, 먼저 세그멘테이션을 통해 논리 주소를 선형 주소로 변환한 후, 페이징을 통해 선형 주소를 최종 물리 주소로 변환한다. 이렇게 함으로써 세그멘테이션의 논리적 보호와 공유 장점과 페이징의 물리 메모리 효율적 관리 장점을 모두 취할 수 있다.
페이지 교체 알고리즘은 가상 메모리 시스템에서 요구된 페이지가 현재 물리 메모리에 존재하지 않을 때, 즉 페이지 부재가 발생했을 때, 어떤 페이지 프레임을 비워서 새로운 페이지를 올려놓을지 결정하는 방법이다. 물리 메모리가 가득 찬 상태에서 새로운 페이지를 로드하려면 기존의 페이지 중 하나를 선택하여 보조기억장치로 내보내야 하는데, 이때 선택 기준을 제공하는 알고리즘이다. 알고리즘의 효율성은 페이지 부재 발생 빈도에 직접적인 영향을 미치며, 이는 전체 시스템 성능을 좌우하는 핵심 요소이다.
주요 페이지 교체 알고리즘은 다음과 같다.
알고리즘 | 설명 | 특징 |
|---|---|---|
FIFO (First-In First-Out) | 가장 먼저 메모리에 로드된 페이지를 교체 대상으로 선택한다. | 구현이 간단하지만, 자주 사용되는 오래된 페이지가 교체될 수 있어 성능이 떨어질 수 있다. 벨레이디의 모순 현상이 발생할 수 있다[5]. |
최적 페이지 교체(OPT) | 앞으로 가장 오랫동안 사용되지 않을 페이지를 교체 대상으로 선택한다. | 가장 낮은 페이지 부재율을 보장하는 이상적인 알고리즘이지만, 미래의 페이지 참조를 알 수 없기 때문에 실제 구현은 불가능하다. 주로 다른 알고리즘의 성능 비교 기준으로 사용된다. |
LRU (Least Recently Used) | 가장 오랫동안 사용되지 않은 페이지를 교체 대상으로 선택한다. | 최근의 과거 참조 기록을 미래의 예측에 활용한다는 점에서 OPT에 근접한 좋은 성능을 보인다. 하지만 참조 기록을 관리하는 하드웨어나 소프트웨어 오버헤드가 존재한다. |
FIFO는 큐를 이용해 간단히 구현할 수 있지만 성능상 한계가 뚜렷하다. 반면 LRU는 실제 시스템에서 가장 널리 채택되는 알고리즘 중 하나로, 카운터나 스택 등의 자료구조를 통해 구현된다. OPT 알고리즘은 실현 가능성은 없지만, 다른 알고리즘의 성능 한계를 평가하는 이론적 척도로 중요하게 활용된다. 이 외에도 LRU의 근사 알고리즘인 2차 기회 알고리즘(Clock Algorithm)이나 LFU(Least Frequently Used) 등 다양한 변형 알고리즘이 존재한다.
FIFO는 가장 간단한 페이지 교체 알고리즘 중 하나이다. 이 알고리즘은 메모리에 적재된 순서대로 페이지를 교체 대상으로 선정한다. 즉, 가장 먼저 들어온 페이지를 가장 먼저 내보낸다. 이 원리는 일반적인 큐(Queue)의 동작 방식과 동일하다.
구현은 매우 단순하다. 운영체제는 페이지 프레임을 순서대로 관리하는 큐를 유지한다. 새로운 페이지가 메모리에 적재되면 큐의 끝에 추가된다. 페이지 부재가 발생하여 교체가 필요해지면, 큐의 맨 앞에 있는, 즉 가장 오래전에 적재된 페이지를 선택하여 디스크로 내보내고 그 자리에 새로운 페이지를 적재한다. 이때 새로 들어온 페이지는 다시 큐의 끝에 위치하게 된다.
그러나 FIFO 알고리즘에는 벨레이디의 모순 현상이라는 명백한 한계가 존재한다. 이는 사용 가능한 페이지 프레임 수를 늘렸음에도 불구하고 페이지 부재 횟수가 오히려 증가할 수 있는 현상을 말한다. 이는 알고리즘이 페이지의 사용 빈도나 최근 사용 여부와 같은 중요한 접근 패턴을 전혀 고려하지 않기 때문에 발생한다. 가장 오래된 페이지가 활발히 사용 중인 중요한 페이지일 가능성을 배제하지 못한다.
따라서 FIFO는 이해하기 쉽고 구현 부담이 적지만, 성능 면에서는 효율적이지 않다. 이 단점을 해결하기 위해 LRU나 클럭 알고리즘과 같이 더 정교한 알고리즘이 개발되어 실제 시스템에서 더 널리 사용된다.
최적 페이지 교체 알고리즘은 페이지 교체 알고이즘 중 하나로, 벨레이디에 의해 1966년 제안되었다. 이 알고리즘은 앞으로 가장 오랫동안 사용되지 않을 페이지를 교체 대상으로 선택한다. 이론적으로 페이지 부재를 최소화할 수 있는 가장 이상적인 알고리즘으로 간주되며, 다른 알고리즘들의 성능을 비교하는 기준으로 사용된다.
그러나 최적 페이지 교체 알고리즘은 실제 시스템에서 구현이 불가능하다. 알고리즘이 작동하려면 프로세스가 미래에 어떤 페이지를 언제 접근할지 미리 알아야 하는데, 이는 일반적으로 불가능하기 때문이다. 따라서 이 알고리즘은 주로 다른 실용적인 알고리즘들의 성능 한계를 분석하는 이론적 모델로 활용된다. 예를 들어, LRU나 FIFO 알고리즘의 성능을 평가할 때 "최적 알고리즘 대비 얼마나 효율적인가"를 측정하는 지표로 쓰인다.
LRU(Least Recently Used)는 페이지 교체 알고리즘 중 하나로, 가장 오랫동안 사용되지 않은 페이지를 교체 대상으로 선택하는 방식이다. 이 알고리즘의 핵심 아이디어는 '시간적 지역성'에 기반한다. 즉, 최근에 접근된 페이지는 가까운 미래에 다시 접근될 가능성이 높다는 관찰을 활용하여, 반대로 가장 오래전에 접근된 페이지는 앞으로 사용될 가능성이 가장 낮을 것이라고 가정한다.
LRU를 구현하기 위해서는 각 페이지가 마지막으로 참조된 시간을 추적해야 한다. 일반적으로 페이지 테이블 항목에 '참조 비트'나 '타임스탬프' 필드를 추가하여 관리한다. 실제 시스템에서는 모든 페이지에 대한 정확한 접근 시간 기록을 유지하는 것이 큰 오버헤드를 유발할 수 있으므로, 근사적인 LRU 알고리즘이 자주 사용된다. 대표적인 근사 알고이름으로 '클럭 알고리즘'이나 '추가 참조 비트 알고리즘'이 있다.
구현 방식 | 설명 | 특징 |
|---|---|---|
카운터/타임스탬프 | 각 페이지 접근 시 시스템 시간 또는 논리적 클럭을 기록 | 구현이 복잡하고, 검색 오버헤드가 큼 |
참조 비트 스택 | 페이지 참조 시 해당 페이지 번호를 스택의 top으로 이동 | 이론적으로 이상적이지만, 하드웨어 지원이 필요 |
클럭 알고리즘(근사 LRU) | 원형 큐와 참조 비트를 사용하여 교체 후보를 순회하며 탐색 | 하드웨어 지원이 간단하고, 실제 시스템에서 널리 사용됨 |
LRU는 이상적인 최적 페이지 교체(OPT) 알고리즘에 근접한 성능을 보이며, FIFO보다 우수한 페이지 부재율을 보이는 경우가 많다. 그러나 완벽한 LRU 구현은 상당한 하드웨어나 소프트웨어 오버헤드를 수반하기 때문에, 성능과 구현 복잡도 사이의 타협점을 찾은 다양한 변형 알고리즘이 개발되고 적용되었다.
가상 메모리는 메모리 추상화를 제공하여 각 프로세스가 독립적이고 연속적인 메모리 공간을 갖는 것처럼 보이게 한다. 이는 프로그래머가 물리적 메모리의 제약이나 다른 프로세스의 메모리 배치를 고려하지 않고 프로그램을 작성할 수 있게 해준다. 동시에, 각 프로세스의 가상 주소 공간은 서로 격리되어 있어, 한 프로세스가 다른 프로세스의 메모리 영역을 함부로 접근할 수 없다. 이는 시스템의 안정성과 보안을 크게 향상시키는 핵심적인 장치이다.
가장 두드러진 장점은 사용 가능한 물리 메모리의 크기를 넘어서는 메모리 공간을 프로세스에 제공할 수 있다는 점이다. 운영체제는 페이징이나 세그멘테이션 기법을 통해 프로세스의 일부만 물리 메모리에 올려놓고, 나머지는 보조기억장치(주로 하드 디스크나 SSD)의 특별한 영역(스왑 공간)에 보관한다. 프로세스가 필요로 하는 데이터가 물리 메모리에 없을 경우(페이지 폴트) 운영체제가 개입하여 필요한 부분을 디스크에서 불러온다. 이를 통해 제한된 물리 메모리로도 더 크고 많은 프로그램의 동시 실행이 가능해진다.
이러한 확장 기능은 메모리 공유와 효율적인 메모리 사용을 촉진한다. 여러 프로세스가 공통으로 사용하는 라이브러리 코드나 데이터(예: C 표준 라이브러리)는 물리 메모리에 단 하나의 사본만 존재하도록 매핑할 수 있다. 또한, 프로세스가 실제로 사용하는 메모리 영역(워킹 셋)만 물리 메모리에 상주시킴으로써, 사용되지 않는 코드나 데이터는 보조기억장치로 내보내어 귀중한 물리 메모리 공간을 절약할 수 있다.
각 프로세스는 운영체제에 의해 할당된 독립적인 가상 주소 공간을 가지며, 이 공간은 일반적으로 0부터 시작하는 연속적인 주소 범위로 구성된다. 이는 프로세스가 실제 물리 메모리의 어느 위치에 로드되었는지, 다른 프로세스의 메모리와 어떻게 공유되는지에 대해 전혀 알 필요가 없음을 의미한다. 이러한 추상화는 프로그래머에게 균일하고 단순화된 메모리 뷰를 제공하여 애플리케이션 개발을 크게 용이하게 한다.
가상 메모리는 강력한 메모리 보호 메커니즘의 기초가 된다. 메모리 관리 장치(MMU)는 모든 메모리 접근 시 가상 주소를 물리 주소로 변환하는 과정에서 접근 권한을 검사한다. 각 페이지 테이블 항목에는 읽기, 쓰기, 실행 권한 비트가 설정되어 있어, 운영체제는 특정 메모리 영역을 읽기 전용, 사용자 모드 접근 금지 또는 커널 전용으로 지정할 수 있다.
이러한 보호는 시스템의 안정성과 보안을 보장하는 데 핵심적이다. 한 프로세스의 오류(예: 잘못된 포인터 접근)가 다른 프로세스나 운영체제 커널의 메모리를 침범하는 것을 방지한다. 또한, 커널 공간은 일반적으로 사용자 프로세스의 접근으로부터 보호되어, 악성 코드나 버그가 시스템 전체를 제어하는 것을 막는다. 권한 검사는 하드웨어 수준에서 수행되므로 매우 효율적이다.
보호 유형 | 설명 | 목적 |
|---|---|---|
공간 분리 | 각 프로세스가 독립적인 주소 공간을 가짐 | 프로세스 간 간섭 및 충돌 방지 |
권한 제어 | 읽기/쓰기/실행 권한을 페이지 단위로 설정 | 잘못된 메모리 쓰기 또는 코드 실행 방지 |
권한 수준 분리 | 사용자 모드와 커널 모드 메모리 영역 분리 | 운영체제 커널 보호 및 시스템 안정성 유지 |
이 추상화와 보호는 다중 프로그래밍 환경에서 여러 애플리케이션이 동시에 안전하고 효율적으로 실행될 수 있는 토대를 마련한다.
가상 메모리는 물리 메모리의 실제 크기보다 훨씬 큰 주소 공간을 프로세스에 제공한다. 이를 통해 애플리케이션은 사용 가능한 RAM 용량에 직접적으로 제약받지 않고 더 큰 프로그램과 데이터 세트를 실행할 수 있다. 이 확장된 공간은 보조기억장치(주로 하드 디스크 드라이브 또는 SSD)의 일부를 스왑 공간으로 활용하여 구현된다.
시스템은 프로세스가 필요로 하는 모든 코드와 데이터를 한꺼번에 물리 메모리에 올리지 않는다. 대신, 현재 실행에 필요한 부분만 페이지 단위로 물리 메모리에 유지하고, 당장 필요하지 않은 페이지는 스왑 공간으로 내보낸다. 이 방식은 요구 페이징으로 알려져 있다. 프로세스가 스왑 공간에 있는 페이지에 접근하려 하면 페이지 폴트가 발생하고, 운영체제는 해당 페이지를 물리 메모리로 불러온 후 실행을 계속한다.
이러한 메커니즘은 사용자에게 거의 무제한에 가까운 메모리 공간을 제공하는 것처럼 보이게 한다. 각 프로세스는 자신만의 독립적인 가상 주소 공간을 가지며, 그 크기는 CPU 아키텍처가 정의하는 주소 비트 수에 의해 결정된다. 예를 들어, 32비트 시스템은 최대 4GB(2^32)의 가상 주소 공간을, 64비트 시스템은 그보다 훨씬 더 큰 공간을 제공한다.
아키텍처 | 주소 비트 수 | 이론적 최대 가상 주소 공간 |
|---|---|---|
32비트 | 32비트 | 4 GB |
64비트 (일반적) | 48비트[6] | 256 TB |
64비트 (완전) | 64비트 | 16 엑사바이트(EB) |
물론, 실제 사용 가능한 공간은 운영체제의 구현과 사용 가능한 스왑 공간의 크기에 의해 제한된다. 그러나 핵심은 애플리케이션이 물리 메모리의 부족을 직접적으로 인식하지 않고, 더 큰 작업을 수행할 수 있는 환경이 조성된다는 점이다. 이는 멀티태스킹 환경에서 여러 개의 대용량 프로그램이 동시에 실행되는 것을 가능하게 하는 기반이 된다.
가상 메모리는 많은 장점을 제공하지만, 구현과 운영 과정에서 필연적으로 발생하는 여러 단점과 오버헤드를 동반한다. 가장 대표적인 문제는 페이지 폴트 처리에 따른 성능 저하다. 프로세스가 접근한 가상 주소가 현재 물리 메모리에 로드되지 않은 페이지를 가리킬 경우, 운영체제는 해당 페이지를 보조기억장치에서 불러와야 한다. 이 디스크 입출력 작업은 메모리 접근에 비해 수천 배 이상 느리기 때문에, 페이지 폴트 발생 빈도가 높아지면 전체 시스템 성능이 급격히 저하된다. 이러한 현상을 스레싱이라고 부른다.
시스템은 가상 메모리 관리를 위해 지속적인 오버헤드를 감수해야 한다. 각 프로세스마다 페이지 테이블을 유지해야 하며, 이는 상당한 메모리 공간을 차지한다. 특히 64비트 시스템에서 전체 주소 공간에 대한 페이지 테이블을 완전히 구성하는 것은 거의 불가능에 가깝기 때문에, 다단계 페이지 테이블과 같은 복잡한 구조가 필요해진다. 주소 변환을 가속화하기 위한 TLB도 하드웨어 비용을 증가시키며, TLB 미스가 발생하면 추가적인 메모리 접근이 필요해 성능 손실이 발생한다.
페이지 교체 알고리즘의 운영에도 오버헤드가 존재한다. LRU나 클럭 알고리즘과 같은 알고리즘은 참조 비트를 추적하고 업데이트하기 위해 소프트웨어 또는 하드웨어 지원이 필요하다. 또한, 메모리 쓰기 작업이 발생한 페이지(더티 페이지)는 교체되기 전에 반드시 디스크에 기록해야 하므로, 추가적인 입출력 부하를 유발한다. 이러한 모든 관리 작업은 운영체제 커널의 복잡성을 증가시키고, 문맥 교환 시 TLB 플러시와 같은 부가 작업을 필요로 한다.
단점/오버헤드 유형 | 설명 | 영향 |
|---|---|---|
페이지 폴트 처리 | 필요한 페이지가 물리 메모리에 없어 디스크에서 불러와야 함 | 디스크 I/O로 인한 심각한 성능 저하, 스레싱 가능성 |
메모리 공간 오버헤드 | 페이지 테이블, 페이지 디렉터리 등 관리 구조가 차지하는 공간 | 사용 가능한 물리 메모리 감소 |
주소 변환 오버헤드 | MMU에 의한 주소 변환, TLB 미스 발생 시 추가 메모리 접근 | 모든 메모리 접근 시 발생하는 고정 오버헤드 |
알고리즘 실행 오버헤드 | 페이지 교체 정책(예: LRU)을 구현하기 위한 참조 비트 관리 | CPU 사이클 소모 |
쓰기 오버헤드 | 더티 페이지를 디스크에 기록해야 하는 부담 | 추가적인 디스크 I/O 발생 |
x86 아키텍처는 초기 16비트 모드부터 세그멘테이션 방식을 사용했으나, 32비트 보호 모드(IA-32)로 전환되면서 페이징을 지원하기 시작했다. 이 아키텍처는 계층적 페이지 테이블 구조를 사용하며, CR3 레지스터가 최상위 페이지 디렉터리의 물리 주소를 가리킨다. x86-64(AMD64)에서는 4단계(및 이후 5단계) 페이징을 도입하여 더 넓은 64비트 가상 주소 공간을 효율적으로 관리한다. 또한 TLB를 활용하여 주소 변환 속도를 높이며, 페이지 크기는 일반적으로 4KB를 기본으로 하며 2MB나 1GB의 대형 페이지도 지원한다.
ARM 아키텍처는 모바일 및 임베디드 시스템에서 널리 사용되며, 가상 메모리 지원은 MMU를 포함한 메모리 관리 시스템 아키텍처에 의존한다. ARMv7-A 아키텍처는 주로 2단계 변환 테이블 기반의 페이징 시스템을 사용한다. 최신 ARMv8-A 아키텍처(64비트)는 4KB, 16KB, 64KB의 다양한 페이지 크기를 지원하며, 변환 테이블은 최대 4단계까지 구성될 수 있다. ARM의 MMU는 주소 변환 외에도 메모리 영역별 접근 권한과 속성을 정의하는 데 중점을 둔다.
다양한 운영체제는 이 하드웨어 기능 위에서 각자의 가상 메모리 관리자를 구현한다. 예를 들어, 리눅스 커널은 x86, ARM, RISC-V 등 다양한 아키텍처에 대해 추상화된 메모리 관리 계층을 제공하며, 공통된 인터페이스로 아키텍처별 차이를 처리한다. 윈도우 NT 커널 역시 하드웨어 추상화 계층(HAL)을 통해 다양한 플랫폼에서 일관된 가상 메모리 서비스를 제공한다.
x86 아키텍처는 가상 메모리를 구현한 대표적인 사례이다. 인텔이 1985년에 출시한 80386 프로세서부터 본격적인 페이징 기반의 가상 메모리 시스템을 도입했다. 이 시스템은 프로세스마다 4GB(2^32바이트) 크기의 선형 가상 주소 공간을 제공하며, 이를 4KB 크기의 페이지로 나누어 관리한다.
가상 주소를 물리 주소로 변환하기 위해 x86은 2단계 또는 3단계의 페이지 테이블 구조를 사용한다. 기본적인 32비트 모드에서는 페이지 디렉터리와 페이지 테이블의 2단계 구조를 채택한다. 가상 주소의 상위 비트는 페이지 디렉터리 내의 인덱스, 중간 비트는 페이지 테이블 내의 인덱스, 하위 비트는 페이지 내 오프셋으로 사용된다. 페이지 테이블 항목(PTE)에는 대상 물리 주소의 상위 비트(페이지 프레임 번호)와 함께 페이지의 접근 권한(읽기/쓰기, 사용자/슈퍼바이저), 존재 비트(Present bit), 그리고 TLB 관리를 위한 Accessed 비트와 Dirty 비트 등의 제어 정보가 포함된다.
x86-64(AMD64) 아키텍처로 확장되면서 가상 주소 공간은 이론상 2^64바이트로 확장되었으나, 일반적으로 구현은 48비트 또는 57비트 주소 공간을 사용한다. 이는 4단계(페이지 맵 레벨4, 페이지 디렉터리 포인터 테이블, 페이지 디렉터리, 페이지 테이블) 또는 5단계의 페이지 테이블 구조를 필요로 한다. 또한, x86은 세그멘테이션과 페이징을 모두 지원하는 하이브리드 메모리 관리 방식을 제공하지만, 현대 운영체제는 주로 페이징만을 사용하고 세그멘테이션은 평평한(flat) 모델로 설정하여 활용한다.
주요 x86 가상 메모리 기능은 다음과 같다.
기능 | 설명 |
|---|---|
PAE(Physical Address Extension) | 32비트 모드에서 물리 주소를 36비트(최대 64GB)까지 확장하는 기술이다. |
PSE(Page Size Extension) | 4KB 페이지 외에 2MB 또는 4MB의 큰 페이지(Huge Page)를 사용할 수 있게 한다. |
NX 비트(Execute Disable bit) | 데이터 페이지에서 코드 실행을 방지하여 보안을 강화한다. |
EPT(Extended Page Tables) | 가상화 환경에서 하이퍼바이저가 게스트 운영체제의 메모리 변환을 관리하도록 지원한다. |
이러한 메모리 관리 하드웨어 지원은 윈도우, 리눅스, BSD 등 현대 x86 기반 운영체제가 프로세스 격리와 대용량 메모리 지원을 구현하는 기반이 되었다.
ARM 아키텍처는 모바일, 임베디드 시스템부터 서버에 이르기까지 광범위한 영역에서 사용되며, 그 가상 메모리 시스템은 MMU를 통한 주소 변환을 핵심으로 한다. 초기 ARM 아키텍처는 간단한 메모리 관리 유닛을 제공했으나, ARMv5 아키텍처부터 도입된 가상 주소 확장(VMSAv5)은 더욱 정교한 2단계 페이지 테이블 변환을 가능하게 했다. 이는 사용자 모드와 커널 모드 간의 메모리 보호를 강화하는 데 기여했다.
ARMv7-A 아키텍처는 하드웨어 가상화 지원을 포함하며, 32비트 가상 주소 공간을 관리한다. 이 아키텍처는 두 가지 주소 변환 체계를 지원하는데, 하나는 고정된 4KB 페이지 크기를 사용하는 변환 테이블 베이스 레지스터(TTBR) 기반의 시스템이고, 다른 하나는 다양한 페이지 크기(1MB, 64KB 등)를 지원하는 섹션 매핑이다. TLB는 주소 변환 성능을 최적화하는 데 중요한 역할을 한다.
ARMv8-A 아키텍처는 64비트 컴퓨팅으로의 전환을 이루며, 가상 메모리 시스템에도 큰 변화를 가져왔다. 이 아키텍처는 AArch64(64비트)와 AArch32(32비트 호환) 실행 상태를 모두 지원한다. AArch64의 가상 메모리 시스템은 4단계 페이지 테이블 계층 구조를 사용하여 최대 48비트의 가상 주소 공간을 관리할 수 있다. 페이지 크기는 4KB, 16KB, 64KB 중에서 선택 가능하며, 이는 운영 체제 설계자에게 유연성을 제공한다.
ARM의 메모리 관리 시스템은 여러 보안 및 특수 기능을 통합한다. 트러스트존 기술은 일반적인 운영 체제 영역과 보안이 중요한 코드 및 데이터를 분리하는 하드웨어 기반 보안 영역을 생성한다. 또한, 메모리 속성 인디케이션(MAIR) 레지스터를 통해 캐시 정책과 메모리 타입을 세밀하게 제어할 수 있다. 이러한 설계는 전력 소모가 중요한 모바일 환경에서 효율성과 성능을 동시에 달성하는 데 기여한다.