Search

Anonymous Page : vm_alloc_page_with_initializer() 구현

익명 페이지

디스크 기반이 아닌 이미지?

이 프로젝트의 이 부분에서는 익명 페이지라고 불리는 디스크 기반이 아닌 이미지를 구현할 것입니다.
디스크 기반 O : 실행파일(.exe)에서 읽어옴
디스크 기반 X : 파일에서 안 읽어옴, 그냥 0으로 채움

백업 파일이나 장치가 없다?

익명 매핑은 백업 파일이나 장치가 없습니다. 명명된 파일 소스가 없기 때문에(파일 지원 페이지와는 달리) 익명이라고 불립니다.
// 파일 지원 페이지 (VM_FILE) page_fault()"program.exe의 0x500 위치에서 읽어와" // 익명 페이지 (VM_ANON) page_fault()"파일 없으니까 그냥 0으로 채워"
C
복사

스택과 힙을 위해 사용

익명 페이지는 실행 파일에서 스택과 힙을 위해 사용됩니다.
// 스택 int main() { int x = 10; // VM_ANON 페이지 } // 힙 char *ptr = malloc(100); // VM_ANON 페이지
C
복사

결론

파일에서 읽을 필요 없이 그냥 0으로 채우면 되는 페이지(마치 스택 & 힙처럼)
파일 페이지 = 책에서 복사한 내용
익명 페이지 = 빈 노드(새 종이)

Page Initialization with Lazy Loading(지연 로딩을 통한 페이지 초기화)

# 페이지 초기화 플로우 1. 프로그램 시작 ↓ 2. vm_alloc_page_with_initializer() 호출 → SPT에 페이지 정보만 등록 (물리 메모리 할당 X) ↓ 3. 사용자 프로그램 실행 계속 ↓ 4. 페이지 접근 시도 ↓ 5. 페이지 폴트 발생! ↓ 6. uninit_initialize() 호출 ↓ 7. anon_initializer() 실행 (익명 페이지인 경우) / file_backed_initializer() 실행(파일 지원 페이지인 경우) ↓ 8. 물리 메모리 할당 & 0으로 채움
Markdown
복사

Lazy Loading for Executable(실행 파일의 지연 로딩)

언제 페이지를 실제로 로드할까?
프로젝트 2 : 프로그램 시작할 때(즉시)
프로젝트 3 : 실제로 사용할 때(지연)

VM_UNINIT(페이지 타입)

모든 페이지는 처음에 VM_UNINIT 페이지로 생성됩니다. 또한 초기화되지 않은 페이지를 위한 페이지 구조체인 include/vm/uninit.hstruct uninit_page를 제공합니다. 초기화되지 않은 페이지를 생성, 초기화, 파괴하는 함수들은 include/vm/uninit.c에서 찾을 수 있습니다.
페이지 생명주기: 생성VM_UNINIT (대기 중)페이지 폴트VM_ANON or VM_FILE (실제 타입)
struct page { const struct page_operations *operations; void *va; /* Address in terms of user space */ struct frame *frame; /* Back reference for frame */ /* Your implementation */ /* Project_3 Memory Management */ struct hash_elem hash_elem; /* 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 }; };
C
복사

실행 흐름 예시

1. 프로그램 시작

userprog/process.c
static bool load_segment(struct file* file, off_t ofs, uint8_t* upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable) { // 이하 생략.... /* TODO: Set up aux to pass information to the lazy_load_segment. */ /* 실제 로딩을 하는건 아니고, 페이지 폴트 발생 시 로딩하도록 페이지만 등록 */ void* aux = NULL; if (!vm_alloc_page_with_initializer(VM_ANON, // 타입 upage, // 가상주소 writable, // 쓸 수 있는지 여부 lazy_load_segment, // 나중에 호출할 함수 aux // 파일 정보(어디서 읽을지) )) return false; // 이하 생략.... }
C
복사

2. vm_alloc_page_with_initializer 내부

bool vm_alloc_page_with_initializer(enum vm_type type, void *upage, bool writable, vm_initializer *init, void *aux) { ASSERT(VM_TYPE(type) != VM_UNINIT); struct supplemental_page_table *spt = &thread_current()->spt; if (spt_find_page(spt, upage) == NULL) { /* TODO: Create the page, fetch the initializer according to the VM type, * and then create "uninit" page struct by calling uninit_new. */ /* TODO: Insert the page into the spt. */ } return false; }
C
복사
TODO 주석에 의하면(우리가 구현해야함) : 1. 페이지 구조체 생성 2. VM_UNINIT 타입의 페이지로 설정 3. SPT에 등록
이 시점에서 :
1.
SPT에 페이지 정보 등록
2.
물리 메모리 할당
3.
데이터 로드
4.
나중에 lazy_load_segment 함수 호출하도록 등록

3. 프로그램 실행 중

// 사용자 프로그램(예시) int main() { printf("Hello"); // 0x1000 주소 접근! }
C
복사

4. 페이지 폴트 발생

userprog/exception.c
// 실제로는 인터럽트 프레임을 인자로 전달받음 page_fault(0x1000) { // 핀토스 코드의 두 번째 인자(fault_addr) vm_try_handle_fault(..., 0x1000, ...); }
C
복사

5. vm_try_handle_fault

vm/vm.c
bool vm_try_handle_fault(struct intr_frame *f, void *addr, bool user, bool write, bool not_present) { struct supplemental_page_table *spt = &thread_current()->spt; struct page *page = NULL; /* TODO: Validate the fault */ /* TODO: Your code goes here */ return vm_do_claim_page(page); }
C
복사
TODO 주석에 의하면(우리가 구현해야함) : 1. SPT에서 해당하는 페이지 찾기 2. 페이지 검증 작업 3. 페이지 클레임(실제 로딩)

6. vm_do_claim_page

static bool vm_do_claim_page(struct page *page) { struct frame *frame = vm_get_frame(); frame->page = page; page->frame = frame; /* TODO: Insert page table entry to map page's VA to frame's PA. */ return swap_in(page, frame->kva); }
C
복사
TODO 주석에 의하면(우리가 구현해야함) : 1. 물리 프레임 할당 2. 연결 3. 실제 데이터 로딩(스왑-인)

7. uninit_initialize (자동 호출)

vm/uninit.c
static const struct page_operations uninit_ops = { .swap_in = uninit_initialize, .swap_out = NULL, .destroy = uninit_destroy, .type = VM_UNINIT, };
C
복사
이렇게 연산 함수와 swap_in 인자가 매핑되어있습니다. 어느 지점에서 이게 매핑된 건지는 아직 모르겠습니다.
아무튼 uninit_initialize 함수와 swap_in이 매핑되어있다보니, vm_do_claim_page 함수에서 swap_in을 호출할 때, uninit_initialize 함수가 자동으로 호출됩니다.
struct uninit_page { /* Initiate the contets of the page */ vm_initializer *init; enum vm_type type; void *aux; /* Initiate the struct page and maps the pa to the va */ bool (*page_initializer) (struct page *, enum vm_type, void *kva); };
C
복사
uninit_initialize (struct page *page, void *kva) { struct uninit_page *uninit = &page->uninit; /* Fetch first, page_initialize may overwrite the values */ vm_initializer *init = uninit->init; void *aux = uninit->aux; /* TODO: You may need to fix this function. */ return uninit->page_initializer (page, uninit->type, kva) && (init ? init (page, aux) : true); }
C
복사
TODO 주석에 의하면(우리가 구현할 내용) : 페이지에 저장된 정보들이 꺼내진 상태에서, 1. init 함수를 호출하기
우리가 2단계(vm_alloc_page_with_initializer 함수가 실행되는 시점)에서 lazy_load_segment 함수를 콜백함수로 등록해뒀습니다. 그래서 init 함수를 실행하는 시점에, lazy_load_segment 함수를 호출합니다.

8. lazy_load_segment

userprog/process.c
static bool lazy_load_segment(struct page* page, void* aux) { /* TODO: Load the segment from the file */ /* TODO: This called when the first page fault occurs on address VA. */ /* TODO: VA is available when calling this function. */ }
C
복사
TODO 주석에 의하면(우리가 구현할 내용) : 1. 파일 정보를 꺼내기 2. 파일에서 정보를 읽고, 정보를 file에 저장?

9. VM_UNINIT → VM_ANON 변환

struct uninit_page { /* Initiate the contets of the page */ vm_initializer *init; enum vm_type type; void *aux; /* Initiate the struct page and maps the pa to the va */ bool (*page_initializer) (struct page *, enum vm_type, void *kva); };
C
복사
enum vm_type { /* page not initialized */ VM_UNINIT = 0, /* page not related to the file, aka anonymous page */ VM_ANON = 1, /* page that realated to the file */ VM_FILE = 2, /* page that hold the page cache, for project 4 */ VM_PAGE_CACHE = 3, // 이하 생략... };
C
복사
static bool uninit_initialize (struct page *page, void *kva) { struct uninit_page *uninit = &page->uninit; /* Fetch first, page_initialize may overwrite the values */ vm_initializer *init = uninit->init; void *aux = uninit->aux; /* TODO: You may need to fix this function. */ return uninit->page_initializer (page, uninit->type, kva) && (init ? init (page, aux) : true); }
C
복사
uninit 페이지를 init 함수의 인자로 전달하면서 초기화하는 과정 중에 vm_typeVM_ANON으로 변환됩니다.