가상메모리는 디스크를 메인메모리의 캐시처럼 사용하는 기법
크게 세 가지 역할을 함.
이 모든 기능의 핵심에 주소 변환이 있음.
물리 주소(PA)
CPU가 실제 메모리(DRAM)의 위치를 가리키는 주소.
가상 주소(VA)
CPU가 사용하는 주소. 실제 물리 주소와는 다름.
CPU 칩
┌───────────────────────────┐
│ CPU --[VA]--> MMU--[PA]-│--> 메인메모리(DRAM)
└───────────────────────────┘
MMU는 페이지 테이블(page table)이라는, 메모리에 저장된 참조테이블을 참조해서 변환을 수행하고, 이 페이지 테이블은 운영체제가 관리함.
핵심은 가상 주소가 물리 주소에 매핑된다는 것!
가상 주소
→ 물리 주소
→ 또는 아직 할당되지 않음
가상메모리는 "디스크에 저장된 N바이트 배열"
(각 바이트 = 고유 가상주소 = 배열 인덱스)
이 배열의 일부가 메인메모리에 "캐시"됨
가상 페이지(Virtual Page, VP)
= VM 시스템이 가상메모리를 관리하기 위해 고정 크기로 나눈 블록 단위
(보통 P = 2ᵖ 바이트)
각 가상 페이지는 아래 세 상태 중 하나임:
① unallocated : 아직 할당조차 안 됨 (데이터도 없고, 디스크 공간도 안 씀)
② cached : 디스크에 있고, 지금 물리메모리(DRAM)에도 올라와 있음
③ uncached : 디스크에는 있는데, 지금 물리메모리엔 없음
디스크는 DRAM보다 매우 느림.
따라서 페이지 폴트를 최대한 줄이는 방향으로 설계함.
: 가상 페이지가 현재 DRAM에 있는지 기록해 둔 표.
PTE(Page Table Entry)가 모인 배열이고 OS가 관리함.
valid = 1
→ DRAM에 있음
→ PTE가 물리 프레임 번호를 가리킴
valid = 0
→ DRAM에 없음
→ 디스크에 있거나 아직 할당되지 않음
PTE의 valid = 1
→ MMU가 바로 PA로 변환.
→ 메모리에 접근
PTE의 valid = 0
→ CPU가 예외 발생
→ 커널의 page fault handler 실행
* 요구 페이징 : 페이지에 실제로 접근할 때 필요한 페이지를 가져오는 방식
: 디스크가 매우 느린데도 VM이 잘 동작하는 이유
프로그램은 한 번에 모든 메모리를 사용하는 것이 아니라
특정 페이지들을 집중적으로 사용하는 경향이 있음.
워킹셋(working set): 자주 사용하는 페이지들의 집합
워킹셋이 물리 메모리보다 작음
→ 페이지 폴트가 적음
→ 대부분의 접근이 히트
워킹셋이 물리 메모리보다 큼
→ 페이지를 계속 내보내고 다시 가져옴
→ 스레싱(thrashing)
→ 성능 급격히 저하
OS가 프로세스마다 독립적인 페이지 테이블을 유지 → 세 가지 이득
① 동일한 레이아웃 (링크/로드 단순화)
② 페이지 단위 공유
③ 간단한 메모리 할당
* mmap 시스템 콜 (자세한 내용은 9.8.4)
: 파일의 일부를 프로세스의 가상 주소 공간에 매핑하는 시스템 콜.
파일의 내용을 미리 전부 메모리에 복사하는 것이 아니라,
파일과 가상 주소 공간을 연결해 둠.
이후 해당 주소에 접근하면 필요한 페이지가 page fault를 통해 메모리에 올라옴.
즉, 파일을 read()로 읽는 대신 메모리에 있는 데이터처럼 접근할 수 있게 해주는 기능
PTE에 접근 권한 비트를 추가로 둠.
MMU가 주소를 변환할 때 접근 권한도 함께 검사함.
등을 막을 수 있음.
SUP
→ 커널 모드에서만 접근 가능한지
READ
→ 읽기 허용 여부
WRITE
→ 쓰기 허용 여부
권한 위반 발생
→ CPU가 예외 발생
→ OS가 처리
→ 일반적으로 프로세스에는 SIGSEGV가 전달됨
가상 주소와 물리 주소를 페이지 단위로 나눠 생각함.
페이지 크기가 같기 때문에 VPO와 PPO는 동일함.
CPU가 VA 생성
↓
VPN으로 PTE 찾음
↓
valid 확인
↓
valid = 1
→ PPN 얻음
→ PPN + VPO로 PA 생성
→ 메모리 접근
valid = 0
→ page fault
→ OS가 처리
페이지 테이블을 매번 메모리에서 확인하면 느림.
그래서 MMU 안에 자주 사용하는 주소 변환을 저장하는 작은 캐시인 TLB를 둠.
TLB
= VPN → PPN 변환 결과를 저장하는 캐시
구조: VPN을 TLBT(태그) + TLBI(인덱스)로 쪼개서 세트에 인덱싱
TLB Hit
→ 변환이 MMU 칩 내부에서 끝남 → 매우 빠름(사이클 단위)
TLB Miss
→ L1 캐시(또는 메모리)에서 PTE를 가져와 TLB에 채움
하나의 거대한 페이지 테이블을 만들면 사용하지 않는 가상 주소 영역까지 관리해야 해서 공간 낭비가 큼.
그래서 페이지 테이블을 여러 단계로 나눔.
가상주소
↓
Level 1 : 이 영역 쓰고있나 확인
↓
Level 2 : 쓰고있는 애들만 L2 페이지 테이블 만듦
↓
실제 페이지(PTE)
장점
단점 : 주소 변환하려면 여러단계의 PTE를 찾아야 함
→ TLB가 해결!
: 자주 사용하는 PTE를 TLB에 저장해두기 때문에 실제로는 매번 L1 → L2를 전부 찾아갈 필요가 없음.
CPU
↓ VA
TLB 확인
↓
├─ Hit → PPN 획득
│
└─ Miss → 페이지 테이블 확인
↓
valid = 0 → Page Fault
valid = 1 → PPN 획득
↓
PPN + VPO
↓
PA
↓
Cache
↓
DRAM
캐시를 가상 주소로 접근할 수도 있고 물리 주소로 접근할 수도 있음.
VIVT
→ Virtual Index / Virtual Tag
→ 빠르지만 aliasing 등의 문제가 있음
PIPT
→ Physical Index / Physical Tag
→ 문제는 적지만 주소 변환을 먼저 해야 함
VIPT
→ Virtual Index / Physical Tag
→ 주소 변환과 캐시 조회를 병렬로 수행할 수 있음
* VIPT에서는 페이지 오프셋이 VA와 PA에서 같다는 점을 이용함.
실제 시스템에서는 지금까지 배운 개념을 TLB, 멀티레벨 페이지 테이블, 캐시 등을 조합해서 사용함.
Core i7
→ 48비트 가상주소
→ 4KB 페이지
→ 다단계 페이지 테이블
Linux는 프로세스의 가상 주소 공간을 영역 단위로 관리함.
코드 / 데이터 / 힙 / 스택 / mmap 영역 등
파일이나 익명 메모리 같은 객체를 프로세스의 가상 주소 공간에 연결하는 것.
: 실제 파일을 가상 메모리와 연결하는 경우.
ex) 실행파일의 .text/.data, 혹은 사용자가 mmap한 임의 파일
: 실제 파일과 연결되지 않은 메모리 영역.
ex) 힙, 스택 초기 영역
→ 이런 페이지를 demand-zero page라고 함.
하나의 물리 페이지를 여러 프로세스가 공유할 수도 있음.
Process 1 ─┐
├──→ 같은 물리 페이지
Process 2 ─┘
처음에는 물리 페이지를 공유하지만, 쓰기 발생 시 해당 페이지만 복사함.
읽기
Process 1 ─┐
├──→ 같은 물리 페이지
Process 2 ─┘
쓰기 발생
↓
Protection Fault
↓
페이지 복사
↓
Process 1 ─→ 원래 페이지
Process 2 ─→ 새 페이지
DRAM이 부족하면 사용하지 않는 페이지를 DRAM에서 내보낼 수 있음.
가상 페이지
↓
RAM
↓ RAM 부족
Swap Out
↓
Swap Space
나중에 다시 필요해지면
Swap Space
↓
Swap In
↓
RAM
특히 익명 페이지는 원본 파일이 없기 때문에, RAM에서 쫓겨날 때 현재 내용을 보존하기 위해 swap space가 사용될 수 있음.
→ COW로 만들어진 private 페이지도 메모리가 부족하면 swap될 수 있음.
: 부모 프로세스의 가상 주소 공간을 자식에게 복제함.
하지만 물리 페이지를 처음부터 전부 복사하지 않음.
부모와 자식이 같은 물리 페이지를 공유
↓
쓰기 발생
↓
페이지 복사
↓
각자의 페이지 사용
→ Copy-on-Write
: 현재 프로세스의 프로그램을 새로운 프로그램으로 교체함.
파일이나 익명 메모리를 프로세스의 가상 주소 공간에 매핑하는 시스템 콜.
mmap()
→ 가상 주소 공간에 영역 생성 + 파일/익명 메모리와 연결
munmap()
→ 매핑 제거
주요 옵션
MAP_SHARED
→ 변경 내용을 공유
MAP_PRIVATE
→ Copy-on-Write
MAP_ANON
→ 파일 없는 익명 메모리
PROT_READ / WRITE / EXEC
→ 접근 권한 설정
가상메모리의 핵심: 가상 주소를 이용해 실제 물리 메모리를 추상화한다!
⇒ 이 구조를 이용해서