load
함수에서load_segment
를 호출if (!load_segment (file, file_page, (void *) mem_page,
read_bytes, zero_bytes, writable))
file_info
구조체에 lazy_load _segment
에 필요한 파일 정보를 넣기while (read_bytes > 0 || zero_bytes > 0) {
/* Do calculate how to fill this page.
* We will read PAGE_READ_BYTES bytes from FILE
* and zero the final PAGE_ZERO_BYTES bytes. */
size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
size_t page_zero_bytes = PGSIZE - page_read_bytes;
/* TODO: Set up aux to pass information to the lazy_load_segment. */
struct file_info *f_info = (struct file_info *)calloc(1, sizeof(struct file_info));
f_info->file = file;
f_info->read_bytes = page_read_bytes;
f_info->zero_bytes = page_zero_bytes;
f_info->ofs = ofs;
if (!vm_alloc_page_with_initializer (VM_ANON, upage,
writable, lazy_load_segment, f_info))
return false;
/* Advance. */
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
upage += PGSIZE;
ofs += page_read_bytes;
}
Lazy Loading
을 위해 uninit
상태의 페이지 생성 (초기화)
페이지 종류에 따라initializer
을 설정한다.
VM_ANON
(익명 페이지) : 파일로부터 매핑되지 않은 페이지로서 커널로부터 할당된 페이지
스택
과 힙
섹션은 익명 페이지로 메모리에 load(할당) 됨VM_FILE
(파일 기반 페이지) : 파일로부터 매핑된 페이지
코드
섹션과 데이터
섹션이 파일기반 페이지로 load 됨file 정보를 spt
에 등록함
4kb 로 끊어서 uninit
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) {
initializer *page_init = NULL;
switch (type)
{
case VM_ANON:
page_init = anon_initializer;
break;
case VM_FILE:
page_init = file_backed_initializer;
default:
break;
}
struct page *new_page = (struct page *)calloc(1, sizeof(PGSIZE));
uninit_new (new_page, upage, init, type, aux, page_init);
new_page->writable = writable;
// insert 반환 값이 NULL 인 경우 `true`
return spt_insert_page(spt, new_page);
}
err:
return false;
}
Supplemental Page Table : page fault와 페이지 관리(자원 관리, 빨리 찾기)를 위해 사용
물리메모리에
load
되지 않은 페이지에 접근할 때,Page Fault
발생 ! 🚨
static bool
load (const char *file_name, struct intr_frame *if_) {
...
// argument passing
argument_stack(arg_list, token_count, if_);
success = true;
...
}
void argument_stack(char **argv, int argc, struct intr_frame *if_){
char *arg_address[128];
// part A: word-align 전까지
for(int i = argc - 1 ; i >= 0; i--){
int arg_i_len = strlen(argv[i]) + 1; >>>>>>>>>> 🚨 page fault 발생 🚨
page_fault
실행!static void
page_fault (struct intr_frame *f) {
...
#ifdef VM
/* For project 3 and later. */
if (vm_try_handle_fault (f, fault_addr, user, write, not_present))
return;
#endif
...
page fault
가 발생한 주소가 kernel_vaddr
인지 확인spt
에서 물리 메모리와 맵핑되지 않은 페이지를 찾기bool
vm_try_handle_fault (struct intr_frame *f, void *addr,
bool user, bool write UNUSED, bool not_present UNUSED) {
// 유효한 주소인지 확인
if(is_kernel_vaddr(addr)){
return false;
}
// stack growth logic...
struct supplemental_page_table *spt = &thread_current ()->spt;
// 접근한 메모리가 물리 페이지에 맵핑되지 않은 경우
if(not_present){
struct page *page = spt_find_page(spt, addr);
if(!page)
return false;
// write 를 시도하지만 && 페이지는 unwritable 인 경우
if(write && !page->writable)
return false;
return vm_do_claim_page (page);
}
return false;
}
static bool
vm_do_claim_page (struct page *page) {
struct frame *frame = vm_get_frame ();
ASSERT(frame && frame->kva)
if(!page || page->frame){
return false;
}
/* Set links */
frame->page = page;
page->frame = frame;
if(!pml4_set_page(thread_current()->pml4, page->va, frame->kva, page->writable))
return false;
return swap_in (page, frame->kva);
}
pml4_set_page : pml4 테이블에 유저가상주소와 커널의 가상주소를 맵핑시키는 함수
< 프레임 할당 >
static struct frame *
vm_get_frame (void) {
struct frame *frame = NULL;
// 프레임 할당
void *pg_ptr = palloc_get_page(PAL_USER);
if (pg_ptr == NULL)
{
// 사용 가능한 페이지가 없는 경우 페이지를 제거하고 반환
// 즉, 사용자 풀 메모리가 가득 찬 경우 메모리 공간을 위해 프레임 제거
}
frame = (struct frame *)malloc(sizeof(struct frame));
frame->kva = pg_ptr;
frame->page = NULL;
ASSERT(frame != NULL);
ASSERT(frame->page == NULL);
return frame;
}
swap_in : 디스크에 있는 페이지를 물리 메모리에
load
하는 것
static const struct page_operations uninit_ops = {
.swap_in = uninit_initialize, >>>>>>>>>>>>>>>>>>>> 🚨 uninit_initialize 🚨
.swap_out = NULL,
.destroy = uninit_destroy,
.type = VM_UNINIT,
};
static bool
uninit_initialize (struct page *page, void *kva) {
struct uninit_page *uninit = &page->uninit;
vm_initializer *init = uninit->init;
void *aux = uninit->aux;
return uninit->page_initializer (page, uninit->type, kva) &&
(init ? init (page, aux) : true); >>>>>>>>>>>>> 🚨lazy_load_segment 함수 실행🚨
// vm_alloc_page_with_initializer (~, ~, ~, lazy_load_segment,~ ))
// uninit_new (~, ~, init, ~, ~, ~);
}
static bool
lazy_load_segment (struct page *page, void *aux) {
struct file_info *f_info = (struct file_info *)aux;
struct file *file = f_info->file;
size_t page_read_bytes = f_info->read_bytes;
size_t page_zero_bytes = f_info->zero_bytes;
off_t ofs = f_info->ofs;
file_seek (file, ofs);
if(file_read (file, page->frame->kva, page_read_bytes) != (int) page_read_bytes) {
palloc_free_page (page->frame->kva);
return false;
}
memset (page->frame->kva + page_read_bytes, 0, page_zero_bytes);
return true;
}
정리해야할 용어들