운영체제Medium#84
가상 메모리(Virtual Memory)란 무엇이며, 페이징(Paging)은 어떻게 동작하나요?
#OS#가상메모리#페이징#메모리관리
힌트
물리 메모리보다 큰 프로그램을 실행할 수 있는 이유를 생각해보세요.
정답 및 해설
가상 메모리(Virtual Memory)란 무엇이며, 페이징(Paging)은 어떻게 동작하나요?
가상 메모리(Virtual Memory)는 프로세스에게 실제 물리 메모리(RAM)보다 더 큰 주소 공간을 제공하는 메모리 관리 기술입니다. 각 프로세스는 독립된 가상 주소 공간을 가지므로 서로의 메모리 영역에 접근할 수 없어 보안과 격리성이 보장됩니다. 실제로 필요한 페이지만 물리 메모리에 적재하고 나머지는 디스크에 보관함으로써 메모리를 효율적으로 활용합니다.
가상 메모리의 필요성
물리 메모리만 사용하는 경우의 문제:
1. 메모리 크기 제한: 실행 가능한 프로세스 수가 RAM 크기에 종속됨
2. 메모리 단편화: 프로세스가 연속된 물리 메모리를 요구
3. 보안 문제: 한 프로세스가 다른 프로세스의 메모리에 접근 가능
4. 메모리 관리 복잡성: 프로그래머가 메모리 배치를 직접 관리해야 함
가상 메모리가 해결하는 것:
- 32비트 프로세스: 4GB 가상 주소 공간 (물리 메모리와 무관)
- 64비트 프로세스: 수 TB의 가상 주소 공간
- 프로세스 간 완전한 격리
- 페이지 단위의 효율적 메모리 관리
페이징(Paging) 기본 개념
주요 용어
가상 메모리
┌────────────────────────────────────────────────────────┐
│ Page 0 │ Page 1 │ Page 2 │ Page 3 │ Page 4 │ Page 5 │
└────────────────────────────────────────────────────────┘
4KB 4KB 4KB 4KB 4KB 4KB
물리 메모리 (RAM)
┌─────────────────────────────────────────┐
│ Frame 0 │ Frame 1 │ Frame 2 │ Frame 3 │
└─────────────────────────────────────────┘
4KB 4KB 4KB 4KB
디스크 (스왑 영역)
┌──────────────────────────────────────────────────────┐
│ Swap Page │ Swap Page │ Swap Page │ Swap Page │ ... │
└──────────────────────────────────────────────────────┘
Page: 가상 메모리를 고정 크기로 분할한 단위 (보통 4KB)
Frame: 물리 메모리를 Page 크기로 분할한 단위
Page Table: 가상 페이지 번호 → 물리 프레임 번호 매핑 테이블
페이지 테이블(Page Table)
구조와 주소 변환
가상 주소 구조 (32비트, 4KB 페이지):
┌─────────────────────────┬───────────────┐
│ 가상 페이지 번호(VPN) │ 페이지 오프셋 │
│ (20비트) │ (12비트) │
└─────────────────────────┴───────────────┘
2^20 = 1M 페이지 2^12 = 4KB
주소 변환 과정:
가상 주소: VPN=5, Offset=100
│
▼ Page Table 조회
Page Table:
VPN 0 → Frame 3 (물리 메모리에 있음)
VPN 1 → Frame 7 (물리 메모리에 있음)
VPN 2 → Disk (스왑 영역에 있음, Present bit=0)
VPN 5 → Frame 2 (물리 메모리에 있음)
│
▼
물리 주소: Frame=2, Offset=100
물리 주소 = Frame번호 × Page크기 + Offset
= 2 × 4096 + 100
= 8292
페이지 테이블 엔트리 구조
각 PTE (Page Table Entry)의 비트 구성:
┌────────┬────────┬─────────┬──────────┬────────────────┐
│ Present│ Dirty │Accessed │Protection│ Frame Number │
│ (P) │ (D) │ (A) │ (R/W/X) │ │
└────────┴────────┴─────────┴──────────┴────────────────┘
Present bit (P): 1 = 물리 메모리에 있음, 0 = 디스크에 있음
Dirty bit (D): 1 = 페이지 수정됨 (스왑 아웃 시 디스크에 기록 필요)
Accessed bit (A): 최근 접근 여부 (페이지 교체 알고리즘에 사용)
Protection: 읽기/쓰기/실행 권한
TLB (Translation Lookaside Buffer)
페이지 테이블 조회의 성능 문제
TLB 없이 메모리 접근:
1. 가상 주소에서 VPN 추출
2. 페이지 테이블 접근 (메모리 접근 1회)
3. 물리 주소 획득
4. 실제 데이터 접근 (메모리 접근 1회)
→ 총 2번의 메모리 접근 (2배 오버헤드)
TLB를 사용한 성능 최적화
TLB: 최근에 사용한 페이지 테이블 엔트리를 캐싱하는 하드웨어
CPU 내부에 위치, 매우 빠름 (수 사이클)
일반적으로 64~1024개 엔트리
TLB Hit (약 99%):
가상 주소 → TLB 조회 → 물리 주소 직접 획득 → 데이터 접근
→ 1번의 메모리 접근
TLB Miss:
가상 주소 → TLB 조회 실패 → 페이지 테이블 접근 → TLB 갱신
→ 물리 주소 획득 → 데이터 접근
→ 2번의 메모리 접근
성능:
유효 메모리 접근 시간 = hit_rate × TLB_time + miss_rate × (TLB_time + PT_access_time)
= 0.99 × 1ns + 0.01 × (1ns + 100ns) ≈ 2ns (vs 200ns for two memory accesses)
페이지 폴트 (Page Fault)
발생 과정
프로세스가 가상 주소에 접근
│
▼
TLB 조회
│
TLB Hit?
┌─────┴─────┐
YES NO
│ │
물리 주소 페이지 테이블 조회
획득 │
Present bit = 1?
┌─────┴─────┐
YES NO
│ │
물리 주소 ★ 페이지 폴트 발생!
획득 │
▼
OS 핸들러 호출
│
디스크에서 페이지 로드
│
빈 프레임에 적재
│
페이지 테이블 갱신
│
프로세스 재개
페이지 폴트 처리 과정 상세
1. CPU가 페이지 폴트 인터럽트 발생
2. 현재 프로세스 상태 저장 (PCB)
3. OS 페이지 폴트 핸들러 실행
4. 해당 가상 주소가 유효한지 확인
- 유효하지 않으면 SIGSEGV (Segmentation Fault) 발생
5. 디스크(스왑 영역)에서 페이지 로드
6. 빈 물리 프레임 확보
- 빈 프레임이 없으면 페이지 교체 알고리즘 실행
7. 페이지 테이블 및 TLB 업데이트
8. 프로세스 재개
Java에서의 페이지 폴트 영향
// 대용량 배열 초기화 시 페이지 폴트 발생
public class PageFaultExample {
// 처음 접근 시 페이지 폴트 발생 → 점차 빨라짐
public static void main(String[] args) {
int size = 1024 * 1024; // 1M int = 4MB
int[] largeArray = new int[size]; // 메모리 할당만, 아직 물리 페이지 미할당
long start = System.nanoTime();
for (int i = 0; i < size; i++) {
largeArray[i] = i; // 처음 접근 시 페이지 폴트 발생
}
System.out.println("소요 시간: " + (System.nanoTime() - start) + "ns");
// 두 번째 실행: 페이지가 이미 메모리에 있으므로 훨씬 빠름
start = System.nanoTime();
for (int i = 0; i < size; i++) {
largeArray[i] = i * 2;
}
System.out.println("두 번째 소요 시간: " + (System.nanoTime() - start) + "ns");
}
}
페이지 교체 알고리즘
물리 메모리가 가득 찼을 때, 어떤 페이지를 내보낼지 결정합니다.
주요 알고리즘
1. OPT (Optimal, 이론적 최적):
미래에 가장 오랫동안 사용되지 않을 페이지 교체
→ 미래를 알 수 없어 실제 구현 불가, 성능 기준점으로 사용
2. FIFO (First In First Out):
가장 오래전에 적재된 페이지 교체
→ Belady's Anomaly: 프레임 수를 늘려도 폴트가 증가할 수 있음
3. LRU (Least Recently Used): 가장 많이 사용
가장 오랫동안 사용되지 않은 페이지 교체
→ 지역성(Locality) 원리에 기반, 성능 우수
→ 구현 비용: 참조 시마다 타임스탬프 또는 스택 갱신 필요
4. LFU (Least Frequently Used):
참조 횟수가 가장 적은 페이지 교체
→ 초기에 많이 사용되고 이후 불필요한 페이지가 남을 수 있음
5. Clock (NRU - Not Recently Used): Linux 근사 LRU
페이지에 Reference bit 사용, 원형으로 순환하며 교체 대상 선택
→ LRU의 근사, 구현 효율적
LRU 알고리즘 예시
물리 프레임 3개, 페이지 참조 순서: 7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3
참조 | 프레임 상태 | 폴트
─────────────────────────────────────
7 | [7, -, -] | ★
0 | [7, 0, -] | ★
1 | [7, 0, 1] | ★
2 | [2, 0, 1] | ★ (7 교체 - 가장 오래 미사용)
0 | [2, 0, 1] | (0이 있음)
3 | [2, 0, 3] | ★ (1 교체)
0 | [2, 0, 3] | (0이 있음)
4 | [4, 0, 3] | ★ (2 교체)
...
총 페이지 폴트 수 계산 후 성능 비교
가상 메모리와 스와핑
물리 메모리 부족 시:
1. 페이지 교체 알고리즘으로 희생 페이지(victim page) 선택
2. Dirty bit = 1이면 디스크(스왑)에 기록
3. 새 페이지 로드
4. 페이지 테이블 업데이트
스래싱(Thrashing):
- 메모리 부족으로 페이지 폴트가 너무 많이 발생
- 페이지 교체에 대부분의 시간을 소비
- CPU 이용률 급감
- 해결: 다중 프로그래밍 정도 감소, 워킹 셋(Working Set) 알고리즘
요약 표
| 개념 | 설명 | 역할 |
|---|---|---|
| 가상 주소 | 프로세스가 사용하는 논리적 주소 | 프로세스 격리, 큰 주소 공간 제공 |
| 물리 주소 | 실제 RAM의 주소 | 실제 데이터 저장 위치 |
| 페이지 (Page) | 가상 메모리의 고정 크기 단위 (4KB) | 가상 메모리 분할 |
| 프레임 (Frame) | 물리 메모리의 고정 크기 단위 (4KB) | 물리 메모리 분할 |
| 페이지 테이블 | VPN → Frame 번호 매핑 | 주소 변환 |
| TLB | 페이지 테이블 캐시 (하드웨어) | 주소 변환 속도 향상 |
| 페이지 폴트 | 요청 페이지가 물리 메모리에 없음 | OS가 디스크에서 로드 |
| 스왑 영역 | 디스크의 임시 메모리 공간 | 물리 메모리 확장 |
| 페이지 교체 알고리즘 | 기준 | 구현 복잡도 | 성능 |
|---|---|---|---|
| OPT | 미래 최장 미사용 | 불가 (이론) | 최적 |
| FIFO | 가장 오래 적재됨 | 낮음 | 낮음 |
| LRU | 가장 오래 미사용 | 높음 | 우수 |
| LFU | 참조 횟수 최소 | 중간 | 중간 |
| Clock | NRU 근사 | 낮음 | LRU와 유사 |