본문으로 건너뛰기

안동민 개발노트

본문 시작

페이징

가상 주소 공간을 페이지와 프레임으로 나누고 페이지 테이블을 이용해 논리 주소를 물리 주소로 변환합니다.

세그먼테이션은 외부 단편화를 완전히 해결하지 못했습니다.

세그먼트 크기가 가변적이므로, 시간이 지나면 프레임의 빈 공간이 흩어지는 문제는 여전합니다.

근본적인 해결책은 메모리를 고정 크기의 작은 블록으로 나누는 것입니다.

이것이 페이징(Paging)이며, 현대 운영체제 메모리 관리의 핵심입니다.

페이징

세그먼테이션은 외부 단편화를 완전히 해결하지 못했습니다. 세그먼트 크기가 가변적이므로, 시간이 지나면 프레임의 빈 공간이 흩어지는 문제는 여전합니다.

  1. 가상 주소를 프레임으로 바꾸는 순서

    실제 RAM이 8GB인 컴퓨터에서 각각 4GB를 요구하는 프로그램 5개를 동시에 실행할 수 있을까요? 2 논리 메모리는 페이지, 물리 메모리는 같은 크기 프레임으로 나뉩니다. 3 각 프로세스는 자신만의 페이지 테이블(Page Table)을 가집니다. 4 CPU가 논리 주소를 생성하면 MMU가 다음 과정을 수행합니다.

  2. 논리 주소와 실제 프레임 분리

    실제 RAM이 8GB인 컴퓨터에서 각각 4GB를 요구하는 프로그램 5개를 동시에 실행할 수 있을까요?

  3. 페이지와 프레임

    논리 메모리는 페이지, 물리 메모리는 같은 크기 프레임으로 나뉩니다.

  4. 페이지 테이블

    각 프로세스는 자신만의 페이지 테이블(Page Table)을 가집니다.

  5. 주소 변환 과정

    CPU가 논리 주소를 생성하면 MMU가 다음 과정을 수행합니다.

  6. 내부 단편화와 페이지 폴트 신호 점검

    페이지 크기는 테이블 크기, 내부 단편화, TLB 효율을 함께 보고 정합니다. 이것이 페이징(Paging)이며, 현대 운영체제 메모리 관리의 핵심입니다. 물리 메모리보다 큰 작업 집합을 다루기 위해 주소 공간과 실제 적재 상태를 분리합니다.


가상 메모리의 필요성

실제 RAM이 8GB인 컴퓨터에서 각각 4GB를 요구하는 프로그램 5개를 동시에 실행할 수 있을까요?

가능합니다.

모든 프로그램이 4GB 전부를 동시에 사용하지는 않기 때문입니다.

가상 메모리(Virtual Memory)는 프로세스에게 물리 메모리보다 큰 주소 공간을 제공하면서, 실제로 사용하는 부분만 물리 메모리에 올리는 기법입니다.

나머지는 디스크에 보관됩니다.

프로세스는 마치 거대한 연속된 메모리를 독점하고 있는 것처럼 동작합니다.

32비트 프로세스는 4GB, 64비트 프로세스는 사실상 무한한 주소 공간을 봅니다.

환상을 만들어내는 핵심 메커니즘이 페이징입니다.

가상 메모리가 제공하는 이점:

  • 물리 메모리 초과 사용: 물리 RAM보다 큰 프로그램을 실행할 수 있습니다.
  • 프로세스 격리: 각 프로세스가 독립된 주소 공간을 가지므로, 다른 프로세스의 메모리를 침범할 수 없습니다.
  • 메모리 공유: 여러 프로세스가 같은 물리 페이지를 공유할 수 있습니다(공유 라이브러리).
  • 보호: 페이지별로 읽기/쓰기/실행 권한을 설정할 수 있습니다.

페이지와 프레임

페이징에서 논리 메모리는 페이지(Page)라는 고정 크기 블록으로, 물리 메모리는 프레임(Frame)이라는 같은 크기의 블록으로 나뉩니다.

일반적인 페이지 크기:

  • 4KB (4,096 바이트): x86, ARM의 기본 페이지 크기. 가장 보편적.
  • 2MB / 1GB: x86의 대규모 페이지(Huge Page). 데이터베이스, 과학 계산에서 사용.
  • 16KB / 64KB: ARM64에서 선택 가능. Apple Silicon(macOS)은 16KB 사용.

프로세스의 페이지들은 물리 메모리의 아무 프레임에나 배치될 수 있습니다.

연속될 필요가 없습니다.

페이지 0은 프레임 5에, 페이지 1은 프레임 100에, 페이지 2는 프레임 3에 배치될 수 있습니다.

이것이 핵심입니다.

외부 단편화가 완전히 사라집니다.

모든 프레임은 크기가 동일하므로, 아무 빈 프레임이나 사용하면 됩니다.

연속된 큰 공간을 찾을 필요가 없습니다.

내부 단편화는 여전히 존재합니다.

프로세스 크기가 72,000바이트이면, 4KB 페이지 17개(69,632바이트)와 불완전한 18번째 페이지(2,368바이트 사용, 1,728바이트 낭비)가 필요합니다.

하지만 최대 낭비는 한 페이지 미만(4KB 미만)이므로 연속 할당의 단편화에 비하면 매우 작습니다.

페이징을 실제 자원 관리 관점에서 보면, 페이지 테이블은 프레임 배치와 접근 판단을 함께 담는 장부입니다.

페이지 테이블 항목

프로세스는 연속된 가상 주소를 보지만, OS는 페이지별로 프레임, 권한, backing store를 따로 관리합니다.

  1. 매 접근마다 변환 비용이 붙습니다

    병목 TLB가 없거나 빗나가면 페이지 테이블 조회가 실제 메모리 접근 앞에 추가됩니다.

  2. page
    자원 장부

    page 프로세스별 번호 공간이며 연속처럼 보입니다. frame 빈 프레임이면 어떤 페이지든 바로 배치합니다. store 유효하지 않은 페이지는 파일 또는 스왑에서 찾습니다.

  3. 상태 전이와 장애 판단

    프레임 번호와 오프셋으로 물리 주소를 만듭니다. 할당 범위 안이면 페이지 폴트로 적재합니다. R/W/X가 맞지 않으면 보호 트랩으로 넘어갑니다. 여러 PTE가 같은 프레임을 가리킬 수 있습니다. 교체 전에 backing store로 써야 합니다. 프로세스 주소 공간 밖이면 접근 오류입니다.


페이지 테이블

각 프로세스는 자신만의 페이지 테이블(Page Table)을 가집니다.

페이지 테이블은 논리적 페이지 번호를 물리적 프레임 번호로 매핑합니다.

논리 주소의 구조

논리 주소는 두 부분으로 나뉩니다.

  • 페이지 번호(p): 상위 비트. 페이지 테이블의 인덱스.
  • 페이지 오프셋(d): 하위 비트. 페이지 내의 바이트 위치.

32비트 시스템에서 페이지 크기가 4KB(2122^{12})이면:

  • 하위 12비트 = 오프셋 (0~4095)
  • 상위 20비트 = 페이지 번호 (220=1,048,5762^{20} = 1,048,576개 페이지)

64비트 시스템(x86-64, 48비트 주소 사용)에서:

  • 하위 12비트 = 오프셋
  • 상위 36비트 = 페이지 번호 (2366872^{36} \approx 687억 개 페이지)

페이지 테이블 엔트리(PTE)

각 엔트리에는 프레임 번호 외에 여러 제어 비트가 포함됩니다.

비트용도
유효 비트(Valid)1: 메모리에 존재, 0: 디스크에 있거나 미할당
보호 비트(Protection)R/W/X 권한
참조 비트(Reference)최근 접근 여부 (페이지 교체 알고리즘에 사용)
더티 비트(Dirty/Modified)수정 여부 (교체 시 디스크에 써야 하는지 판단)
캐시 비트캐시 활성화 여부 (I/O 매핑 메모리에서 비활성화)

PTE의 비트들은 단순한 부가 정보가 아니라 접근 허용, 페이지 폴트, 교체 비용을 결정하는 작은 제어판입니다.

페이지 테이블 엔트리: 작은 접근 제어판

프레임 번호만 저장하는 것이 아니라, 접근 가능 여부와 교체 비용까지 매 메모리 접근에서 허용 여부와 교체 우선순위를 결정하게 합니다.

  1. 0이면 페이지 폴트

    Valid 0이면 페이지 폴트 프레임 번호가 있어도 유효하지 않으면 OS가 디스크나 미할당 상태를 확인합니다.

  2. 권한이 맞아야 진행

    R/W/X 권한이 맞아야 진행 쓰기 금지 페이지에 쓰면 보호 위반 트랩이 발생하고, COW도 여기서 시작됩니다.

  3. 최근 사용 흔적

    Reference 최근 사용 흔적 Clock 같은 근사 LRU 알고리즘이 이 비트를 보고 내보낼 후보를 고릅니다.

  4. 수정된 페이지 표시

    Dirty 수정된 페이지 표시 1이면 교체 전에 디스크에 써야 하므로 page out 비용이 커집니다.


주소 변환 과정

페이징에서는 주소 변환, 테이블 갱신, 메모리/디스크 비용을 확인합니다.

가상 주소는 TLB와 페이지 테이블을 거쳐 프레임 주소가 된다

상위 비트는 페이지 번호로 변환되고 하위 offset은 그대로 유지된다. TLB hit면 page table 접근을 건너뛴다.

  1. VPN + offset

    VA VPN + offset 가상 주소 분해

  2. VPN -> PFN

    TLB VPN -> PFN 빠른 캐시 조회

  3. miss 처리

    page table miss 처리 PTE에서 프레임 확인

  4. PFN + offset

    PA PFN + offset 물리 주소 완성

CPU가 논리 주소를 생성하면 MMU가 다음 과정을 수행합니다.

  1. 논리 주소에서 페이지 번호 p와 오프셋 d를 추출합니다.
  2. 페이지 번호 p로 페이지 테이블을 조회하여 프레임 번호 f를 얻습니다.
  3. 물리 주소 = f×페이지크기+df \times \text{페이지크기} + d
address_translation.py
PAGE_SIZE = 4096  # 4KB

def translate(logical_addr, page_table):
    """논리 주소를 물리 주소로 변환"""
    page_number = logical_addr >> 12           # 상위 비트 = 페이지 번호
    offset = logical_addr & 0xFFF              # 하위 12비트 = 오프셋

    if page_number >= len(page_table):
        raise Exception(f"Invalid page: {page_number}")

    entry = page_table[page_number]
    if not entry["valid"]:
        raise Exception(f"Page fault at page {page_number}")

    frame_number = entry["frame"]
    physical_addr = (frame_number << 12) | offset
    return physical_addr

# 페이지 테이블 예제
page_table = [
    {"frame": 5,   "valid": True},   # 페이지 0 → 프레임 5
    {"frame": 8,   "valid": True},   # 페이지 1 → 프레임 8
    {"frame": 1,   "valid": True},   # 페이지 2 → 프레임 1
    {"frame": 0,   "valid": False},  # 페이지 3 → 디스크 (page fault)
]

# 논리 주소 8200 = 페이지 2 (8200 // 4096 = 2), 오프셋 8
logical = 8200
physical = translate(logical, page_table)
print(f"논리: {logical} → 물리: {physical}")
# 페이지 2 → 프레임 1, 물리 = 1*4096 + 8 = 4104

# 논리 주소 0x3100 → 페이지 3 → page fault!
try:
    translate(0x3100, page_table)
except Exception as e:
    print(f"예외: {e}")

페이지 크기 선택의 트레이드오프

작은 페이지(4KB): 내부 단편화가 적습니다.

하지만 페이지 테이블이 커지고, TLB 미스가 자주 발생합니다.

큰 페이지(2MB, 1GB): 페이지 테이블이 작아지고, TLB 적중률이 높아집니다.

하지만 내부 단편화가 커지고, 세밀한 메모리 보호가 어렵습니다.

페이지 크기를 고를 때 실제로 함께 움직이는 비용은 아래처럼 정리할 수 있습니다.

페이지 크기 영향

페이지와 프레임은 항상 같은 크기입니다. 크기를 키우면 변환 메타데이터는 줄지만, 한 번에 낭비되거나 보호되는 단위도 함께 커집니다.

  1. 세밀한 기본 단위

    4KB 세밀한 기본 단위 마지막 페이지의 내부 낭비가 작습니다. 권한과 공유를 작은 범위로 나눌 수 있습니다. 큰 주소 공간에서는 PTE 수와 TLB 압박이 커집니다.

  2. TLB 도달 범위 확대

    2MB TLB 도달 범위 확대 한 TLB 엔트리가 4KB 페이지 512개 분량을 덮습니다. 데이터베이스처럼 넓게 순회하는 워크로드에 유리합니다. 작은 객체가 섞이면 낭비와 권한 단위가 거칠어집니다.

  3. 극단적으로 큰 매핑

    1GB 극단적으로 큰 매핑 페이지 테이블 계층과 TLB 미스를 크게 줄일 수 있습니다. 대용량 캐시, 분석 작업처럼 긴 연속 영역에 맞습니다. 할당 실패와 내부 단편화 위험도 가장 큽니다.

현대 시스템은 기본 4KB + 선택적 대규모 페이지를 지원합니다.

Linux의 Transparent Huge Pages(THP)는 자동으로 2MB 페이지를 활용합니다.

데이터베이스(Oracle, PostgreSQL)는 성능을 위해 명시적으로 HugePages를 설정합니다.

이 변환은 모든 메모리 접근에서 발생합니다.

하나의 명령어를 실행하는 데도 여러 번의 메모리 접근이 필요하므로, 변환 속도가 매우 중요합니다.

다음 절에서는 이 변환을 빠르게 만드는 TLB와 페이지 테이블 최적화를 다루겠습니다.

페이징 주소 착시

프로세스는 연속된 가상 주소를 보지만, 실제 물리 메모리는 프레임 단위로 흩어져 있습니다. 변환 표가 이 간극을 메웁니다.

  1. 01

    Virtual page

  2. 02

    Page table

  3. 03

    Frame

  4. 04

    Offset

  5. 05

    Physical address

  6. 페이지 가상 주소 공간

    고정 크기 블록으로 나눈 단위입니다.

  7. 프레임 물리 메모리

    같은 크기로 나눈 칸이라 어떤 페이지든 올라갈 수 있습니다.

  8. PTE/TLB 페이지 테이블 항목

    TLB가 변환 비용과 권한 확인을 함께 다룹니다.

  9. 페이지 폴트 필요한 페이지

    메모리에 없으면 디스크에서 가져오고 실행이 잠시 멈춥니다.

페이징은 고정 크기 매핑으로 연속 배치 요구를 없앤다

페이징은 논리 주소 공간을 페이지로, 물리 메모리를 프레임으로 쪼개 같은 크기끼리 매핑합니다. 프로세스가 물리적으로 이어진 공간을 차지할 필요가 없어 외부 단편화를 크게 줄입니다.

  1. 논리 주소 8292

    page=2 · offset=100 page size 4096B

  2. Page table[2]

    frame=7 · valid=1 · R/W 페이지와 프레임은 같은 크기

  3. 물리 주소

    × 4096 + 100 = 28772 offset 100은 그대로 유지

  4. 주소 분류

    논리 주소는 page number와 offset으로 나뉘고 offset은 프레임 안 위치로 그대로 유지됩니다. address

  5. 페이지 테이블을 조회한다

    page number로 frame number와 권한, valid bit를 찾아 실제 물리 주소를 만듭니다. page table

  6. 분산 배치를 허용한다

    각 페이지는 서로 다른 프레임에 놓일 수 있어 이어진 물리 공간이 필요 없습니다. non-contiguous

  7. TLB로 비용 축소

    매번 페이지 테이블을 메모리에서 찾으면 느리므로 최근 변환을 TLB에 캐시합니다. cache