본문 바로가기

정글캠프-WIL/서브아이템

Pintos - VM [vm.h 학습]

VM_TYPE

vm_type : Pintos VM에서 "이 가상 페이지가 어떻게 관리해야 하는지"를 담당
Pintos VM의 전체 구조
가상주소(VA)
   ↓
PML4 (HW page table)
   ↓
물리 frame (PA)

+ 추가 정보는 SPT가 관리
역할 담당
주소 변환 PML4
페이지의 의미/상태 SPT + struct page
실제 물리 메모리 frame
물리 메모리 관리 frame table

1. PML4

PML4는 CPU/MMU가 사용하는 실제 페이지 테이블

역할은 딱 하나

가상주소 -> 물리주소 변환

하지만 PML4는 이런건 모름
ⓐ. 이 페이지가 mmap인지
ⓑ. swap-out 되었는지
ⓒ. lazy loading 대상인지
ⓓ. stack 인지
ⓔ. file-backed인지
ⓕ. 어디서 다시 읽어와야 하는지
즉, "주소 변환"만 가능하고 "운영체제 정책 정보"는 저장 못함

그래서 필요한 것 : SPT

2. Supplemental Page Table (SPT)

SPT는 운영체제가 따로 관리하는 테이블

여기엔 struct page들이 들어감

SPT는 아래 정보를 저장 (파일이)
 - anon인가
 - file-backed인가
 - swap에 있나
 - dirty한가
 - 어디서 다시 읽어와야 하나?
 - 현재 frame이 있나?

즉 PML4의 부족한 정보를 보충(supplement)하기 위한 테이블

3. 그럼 vm_type은 어디서 쓰이나?

struct page 안에서 사용

struct page {
	const struct page_operations *operations;
	void *va;              /* Address in terms of user space */
	struct frame *frame;   /* Back reference for frame */

	/* Your implementation */

	/* Per-type data are binded into the union.
	 * Each function automatically detects the current union */
	union {
		struct uninit_page uninit;
		struct anon_page anon;
		struct file_page file;
#ifdef EFILESYS
		struct page_cache page_cache;
#endif
	};
};

즉, SPT 안의 page 객체가 "나는 어떤 종류 페이지인가?"를 나타내는 값

페이지 타입 page fault 시
anon swap/file 없이 새 페이지
file 파일에서 읽어야 함
mmap 파일과 동기화 필요
uninit lazy load 필요

4. enum vm_type

ⓐ. VM_UNINIT

초기화 안된 페이지
가상주소만 존재하고 아직 실제 frame은 없음 (lazy loading 상태)
예시

ELF 로딩 시 코드 영역 전체를 RAM에 올리지 않음
대신 "나중에 fault 나면 여기서 읽어"라는 정보만 저장

그래서 page fault 발생 시
UNINIT -> 실제 타입으로 변환
VM_UNINIT
   ↓ first fault
VM_FILE

ⓑ. VM_ANON

anonymous page
파일과 연결되지 않은 페이지
ex. stack, heap, malloc, anonymous mmap

이 페이지는 eviction 시
swap disk로 감 (원본 파일이 없기 때문)

page out -> swap
page in -> swap에서 복구

ⓒ. VM_FILE

파일 기반 페이지
ex. ELF code, mmap file

이 페이지는 eviction 시
dirty면 -> 파일에 write back
clean이면 -> 그냥 버림 (원본 파일이 이미 존재하기 때문)

ⓓ. VM_PAGE_CACHE

FileSystem용 페이지 캐시 (파일 시스템 캐시용)
disk block cache 같은 역할

5. MARKER 비트가 있는 이유

VM_MARKER_0 = (1 << 3) : 추가 상태 비트

기본 타입 + 추가 속성을 비트 OR로 표현 가능

#define VM_STACK (VM_ANON | VM_MARKER_0)

보통 stack 표시 등에 사용 (anon인데 stack 페이지다)

Page & Frame

page : 가상 페이지의 관리자 ( pml4가 모르는 정보들을 담기위해 page가 존재 )
frame : 실제 RAM 4KB 공간의 관리자 ( kva는 실제 물리 프레임을 커널이 접근하기 위한 주소 )
struct page                  struct frame
+-----------+                +-----------+
| va        |  user page     | kva       | physical frame
| frame ----+--------------> | page      |
+-----------+                +-----+-----+
                                  |
                                  v
                            back reference
page는 있어도 frame은 없을 수 있음
예를 들어 lazy loading 상태
SPT에는 page가 있는데, 아직 RAM에는 안 올라옴 [page->frame == NULL]
이 상태에서 유저가 해당 주소에 접근하면 page fault 발생

이 때
1. SPT에서 page 찾음
2. frame 하나 할당
3. 파일/swap/zero page에서 내용 채움
4. page->frame 연결
5. PML4에 va -> frame->kva 매핑

1. 왜 union 구조일까?

struct page는 "부모 클래스"처럼 쓰임
왜냐하면 페이지 종류마다 필요한 정보가 다르기 때문

예시
uninit_page : 아직 로딩 안 된 페이지 (initializer, aux 같은 lazy loading 정보 필요)
anon_page : swap slot 정보 필요
file_page : file 객체, offset, read_bytes, zero_bytes 정보 필요
한 page는 동시에 anon이면서 file 일 수 없기 때문

그래서 union을 써서 필요한 타입 하나의 정보만 저장해서 메모리 절약

2. Operations는 무엇일까?

const struct page_operations *operations;
타입별 함수 테이블
C에서 객체지향을 흉내 내는 방식
예를 들어 page 타입에 따라
swap_in, swap_out, destroy 동작이 달라짐

1. anon page의 swap_out : swap disk에 씀
2. file page의 swap_out : dirty면 파일에 write back
3. uninit page의 swap_in : initializer 실행

실제로 page->operations->swap_in(page, kva)처럼 호출하면, 실제 타입에 맞는 함수가 실행되게 설계

3. 전체 흐름

user가 0x8048000 접근
→ PML4에 매핑 없음
→ page fault
→ 커널은 SPT에서 page를 찾음 spt_find_page(0x8048000)

SPT
 └── page
      va = 0x8048000
      operations = uninit_ops
      frame = NULL
      union.uninit = "파일의 어느 offset에서 읽을지"

→ frame 할당 후
page
  va = 0x8048000
  frame ─────┐
             v
frame
  kva = 0x... 
  page ─────┘

→ PML4 에는 [0x8048000 → frame->kva 매핑이 생김]

4. eviction 될 때도 둘 다 필요

RAM이 부족하면 frame table에서 victim frame을 고름
victim frame
   ↓
frame->page 로 어떤 가상 페이지인지 찾음
   ↓
page type 확인
   ↓
VM_ANON이면 swap으로 내보냄
   ↓
VM_FILE이면 dirty 여부 보고 파일에 write back
   ↓
PML4 매핑 제거 (하지만 SPT에 페이지는 남아 있음)
page->frame = NULL
frame->page = NULL
[나중에 다시 접근하면 page fault가 나고, SPT의 page 정보를 보고 다시 page in 해야 하므로]