PINTOS - Project 3 : Lazy Loading

JinJinJara·2023년 10월 16일
0

PINTOS

목록 보기
9/10

1. load_segment 호출

  • elf 파일 열고 메타테이터 열어 load 한다.
  • load 함수에서load_segment 를 호출
if (!load_segment (file, file_page, (void *) mem_page,
								read_bytes, zero_bytes, writable))

2. lazy loading 을 위해 aux 에 파일 정보 넣어 전달

  • 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;
	}

3. 페이지 할당 및 초기화

  • Lazy Loading을 위해 uninit 상태의 페이지 생성 (초기화)

    • uninit 페이지 Initialize 후 페이지 폴트가 일어나기 전에는 해당 페이지에 맵핑 되는 데이터가 메모리에 올라오지 않는다.
  • 페이지 종류에 따라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와 페이지 관리(자원 관리, 빨리 찾기)를 위해 사용


4. 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
	...

5. page fault handle

  • 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;

}

6. 프레임 할당 및 맵핑

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;

}

7. 물리 메모리에 load

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, ~, ~, ~);
}

8. lazy_load_segment 실행

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;
}

정리해야할 용어들

  • 유저모드와 커널모드
  • 유저풀과 커널풀
  • palloc, malloc, calloc
  • 유니온과 함수 포인터

0개의 댓글