가상 메모리 구현 방식으로 세그멘테이션과 페이징 두 가지가 있습니다. 현대 OS는 페이징을 주로 사용하지만, 왜 세그멘테이션이 밀렸는지 이해하면 메모리 관리의 전체 그림이 보입니다.


세그멘테이션 (Segmentation)

프로세스의 메모리를 논리적 단위(세그먼트)로 나누는 방식 입니다.

세그멘테이션과 페이징 비교

PLAINTEXT
프로세스의 논리적 구조:
┌──────────┐
│ Code 세그먼트   │ → 세그먼트 0
├──────────┤
│ Data 세그먼트   │ → 세그먼트 1
├──────────┤
│ Stack 세그먼트  │ → 세그먼트 2
├──────────┤
│ Heap 세그먼트   │ → 세그먼트 3
└──────────┘

각 세그먼트는 크기가 다를 수 있고, 의미 있는 단위로 나뉩니다.

세그먼트 테이블

논리 주소 = (세그먼트 번호, 오프셋)

PLAINTEXT
세그먼트 테이블:
| 세그먼트 번호 | Base(시작 주소) | Limit(크기) |
|-------------|---------------|------------|
| 0 (Code)    | 0x1000        | 0x400      |
| 1 (Data)    | 0x2000        | 0x300      |
| 2 (Stack)   | 0x5000        | 0x200      |

주소 변환:
논리 주소 (세그먼트 1, 오프셋 0x50)
→ 오프셋(0x50) < Limit(0x300)? Yes
→ 물리 주소 = Base + 오프셋 = 0x2000 + 0x50 = 0x2050

장점:

  • 논리적 의미 단위로 메모리 관리 — Code는 읽기 전용, Stack은 성장 가능
  • 세그먼트별 보호(읽기/쓰기/실행 권한) 설정 가능
  • 공유가 쉬움 — 두 프로세스가 같은 Code 세그먼트를 공유 가능

단점:

  • 외부 단편화 발생 — 세그먼트 크기가 다양하므로 빈 공간이 조각남
  • 세그먼트가 커지면 재배치 필요

페이징 (Paging)

메모리를 고정 크기 블록(페이지)으로 나누는 방식 입니다. 논리적 의미와 무관하게 균일한 크기로 나눕니다.

PLAINTEXT
가상 메모리:                    물리 메모리:
┌────────┐                   ┌────────┐
│ 페이지 0 │ ──────────────→ │ 프레임 3 │
├────────┤                   ├────────┤
│ 페이지 1 │ ─────────→      │ 프레임 0 │ ← 페이지 2
├────────┤             │     ├────────┤
│ 페이지 2 │ ──→        │     │ 프레임 1 │ ← 페이지 3
├────────┤     │       │     ├────────┤
│ 페이지 3 │ ─  │       └──→ │ 프레임 2 │ ← 페이지 1
└────────┘  │  │             ├────────┤
            │  └──────────→  │ 프레임 3 │ ← 페이지 0
            └─────────────→  │ 프레임 4 │ ← 페이지 3 (X)
                             └────────┘
  • 페이지: 가상 메모리의 고정 크기 블록 (보통 4KB)
  • 프레임: 물리 메모리의 고정 크기 블록 (페이지와 같은 크기)
  • 페이지 테이블: 페이지 번호 → 프레임 번호 매핑

주소 변환

PLAINTEXT
가상 주소 = (페이지 번호, 오프셋)

페이지 크기가 4KB(2^12)일 때:
가상 주소 0x3A7C
= 페이지 번호: 0x3A7C / 4096 = 3
= 오프셋: 0x3A7C % 4096 = 0xA7C

페이지 테이블에서 페이지 3 → 프레임 7
물리 주소 = 프레임 7 × 4096 + 0xA7C = 0x7A7C

장점:

  • 외부 단편화 없음 — 고정 크기이므로 빈 공간이 정확히 맞음
  • 비연속 할당 가능 — 물리 메모리에서 연속일 필요 없음
  • 구현이 하드웨어(MMU)로 효율적

단점:

  • 내부 단편화 — 마지막 페이지에 낭비 공간 (최대 4KB - 1)
  • 페이지 테이블 자체가 메모리를 차지함
  • 논리적 의미 단위와 무관한 분할

세그멘테이션 vs 페이징 비교

항목세그멘테이션페이징
분할 단위가변 크기 (논리적)고정 크기 (4KB 등)
외부 단편화있음없음
내부 단편화없음있음 (마지막 페이지)
주소 구조(세그먼트 번호, 오프셋)(페이지 번호, 오프셋)
공유/보호세그먼트 단위로 쉬움페이지 단위로 가능하지만 의미 경계와 안 맞을 수 있음
프로그래머 인식논리적 구조 반영프로그래머에게 투명
현대 OS거의 안 씀주로 사용

세그먼티드 페이징 (Segmented Paging)

세그멘테이션과 페이징을 결합한 방식입니다. 세그먼트를 다시 페이지로 나눕니다.

PLAINTEXT
논리 주소 = (세그먼트 번호, 페이지 번호, 오프셋)

1단계: 세그먼트 번호 → 해당 세그먼트의 페이지 테이블
2단계: 페이지 번호 → 물리 프레임
3단계: 프레임 + 오프셋 = 물리 주소
  • 세그먼트의 논리적 분리 + 페이징의 외부 단편화 제거
  • x86 아키텍처가 이 방식을 지원했지만, 현대 OS(리눅스 등)는 세그멘테이션을 최소화하고 사실상 순수 페이징만 사용

x86에서 세그멘테이션의 현재

PLAINTEXT
x86-64 (64비트):
- 세그먼테이션은 사실상 비활성화
- CS, DS 세그먼트 레지스터는 존재하지만 base = 0, limit = 전체
- 메모리 보호는 페이지 테이블의 권한 비트로 처리

리눅스가 세그멘테이션을 안 쓰는 이유:

  • 페이징만으로 메모리 보호/가상화 충분
  • 세그멘테이션은 하드웨어 호환성을 복잡하게 만듦
  • RISC 아키텍처(ARM 등)는 아예 세그멘테이션 미지원

페이지 테이블의 문제와 해결

문제: 페이지 테이블이 너무 크다

32비트 주소 공간, 4KB 페이지:

  • 페이지 수 = 2^32 / 2^12 = 2^20 = 약 100만 개
  • 각 엔트리 4바이트 → 페이지 테이블 크기 = 4MB (프로세스마다!)

해결 1: 다단계 페이지 테이블

PLAINTEXT
2단계 페이지 테이블:
가상 주소: [디렉토리 인덱스 | 페이지 인덱스 | 오프셋]

1단계: 페이지 디렉토리 → 페이지 테이블의 위치
2단계: 페이지 테이블 → 프레임 번호

사용하지 않는 영역의 페이지 테이블은 만들지 않아도 됨 → 메모리 절약

해결 2: TLB (Translation Lookaside Buffer)

  • 최근 변환 결과를 캐싱하는 하드웨어
  • TLB 히트 시 페이지 테이블 접근 불필요 → 주소 변환 속도 향상
  • 일반적으로 TLB 히트율 95% 이상

핵심 포인트

  • 현대 OS가 세그멘테이션을 안 쓰는 이유: 외부 단편화 + RISC 미지원 + 페이징으로 충분
  • 페이징의 내부 단편화가 심각하지 않은 이유: 최대 낭비가 4KB - 1 = 한 페이지 미만
  • TLB와 컨텍스트 스위칭 관계: 프로세스 전환 시 TLB 플러시 → 성능 저하

정리

세그멘테이션은 논리적으로 깔끔하지만 외부 단편화가 치명적입니다. 페이징은 고정 크기로 외부 단편화를 제거했고, 다단계 페이지 테이블과 TLB로 오버헤드도 해결했습니다. 현대 OS는 사실상 페이징 일색이지만, 실제로는 두 방식을 비교하고 왜 페이징이 승리했는지를 설명할 수 있어야 합니다.

댓글 로딩 중...