diff --git a/pintos/include/vm/vm.h b/pintos/include/vm/vm.h index 496c89d..c2de70e 100644 --- a/pintos/include/vm/vm.h +++ b/pintos/include/vm/vm.h @@ -1,33 +1,34 @@ #ifndef VM_VM_H #define VM_VM_H #include -#include "threads/palloc.h" + #include "lib/kernel/hash.h" +#include "threads/palloc.h" 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, - - /* Bit flags to store state */ - - /* Auxillary bit flag marker for store information. You can add more - * markers, until the value is fit in the int. */ - VM_MARKER_0 = (1 << 3), - VM_MARKER_1 = (1 << 4), - - /* DO NOT EXCEED THIS VALUE. */ - VM_MARKER_END = (1 << 31), + /* 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, + + /* Bit flags to store state */ + + /* Auxillary bit flag marker for store information. You can add more + * markers, until the value is fit in the int. */ + VM_MARKER_0 = (1 << 3), + VM_MARKER_1 = (1 << 4), + + /* DO NOT EXCEED THIS VALUE. */ + VM_MARKER_END = (1 << 31), }; -#include "vm/uninit.h" #include "vm/anon.h" #include "vm/file.h" +#include "vm/uninit.h" #ifdef EFILESYS #include "filesys/page_cache.h" #endif @@ -42,29 +43,33 @@ struct thread; * uninit_page, file_page, anon_page, and page cache (project4). * DO NOT REMOVE/MODIFY PREDEFINED MEMBER OF THIS STRUCTURE. */ struct page { - const struct page_operations *operations; - void *va; /* Address in terms of user space */ - struct frame *frame; /* Back reference for frame */ - - /* Your implementation */ - 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; + const struct page_operations* operations; + void* va; /* Address in terms of user space */ + struct frame* frame; /* Back reference for frame */ + + /* Your implementation */ + struct hash_elem hash_elem; + + bool writable; + + /* 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; + struct page_cache page_cache; #endif - }; + }; }; /* The representation of "frame" */ struct frame { - void *kva; - struct page *page; + void* kva; + struct page* page; + // 추가 : 리스트 연결을 위한 멤버 + struct list_elem frame_elem; }; /* The function table for page operations. @@ -72,44 +77,42 @@ struct frame { * Put the table of "method" into the struct's member, and * call it whenever you needed. */ struct page_operations { - bool (*swap_in) (struct page *, void *); - bool (*swap_out) (struct page *); - void (*destroy) (struct page *); - enum vm_type type; + bool (*swap_in)(struct page*, void*); + bool (*swap_out)(struct page*); + void (*destroy)(struct page*); + enum vm_type type; }; -#define swap_in(page, v) (page)->operations->swap_in ((page), v) -#define swap_out(page) (page)->operations->swap_out (page) +#define swap_in(page, v) (page)->operations->swap_in((page), v) +#define swap_out(page) (page)->operations->swap_out(page) #define destroy(page) \ - if ((page)->operations->destroy) (page)->operations->destroy (page) + if ((page)->operations->destroy) (page)->operations->destroy(page) /* Representation of current process's memory space. * We don't want to force you to obey any specific design for this struct. * All designs up to you for this. */ struct supplemental_page_table { - struct hash hash_table; + struct hash hash_table; }; #include "threads/thread.h" -void supplemental_page_table_init (struct supplemental_page_table *spt); -bool supplemental_page_table_copy (struct supplemental_page_table *dst, - struct supplemental_page_table *src); -void supplemental_page_table_kill (struct supplemental_page_table *spt); -struct page *spt_find_page (struct supplemental_page_table *spt, - void *va); -bool spt_insert_page (struct supplemental_page_table *spt, struct page *page); -void spt_remove_page (struct supplemental_page_table *spt, struct page *page); - -void vm_init (void); -bool vm_try_handle_fault (struct intr_frame *f, void *addr, bool user, - bool write, bool not_present); +void supplemental_page_table_init(struct supplemental_page_table* spt); +bool supplemental_page_table_copy(struct supplemental_page_table* dst, + struct supplemental_page_table* src); +void supplemental_page_table_kill(struct supplemental_page_table* spt); +struct page* spt_find_page(struct supplemental_page_table* spt, void* va); +bool spt_insert_page(struct supplemental_page_table* spt, struct page* page); +void spt_remove_page(struct supplemental_page_table* spt, struct page* page); + +void vm_init(void); +bool vm_try_handle_fault(struct intr_frame* f, void* addr, bool user, bool write, bool not_present); #define vm_alloc_page(type, upage, writable) \ - vm_alloc_page_with_initializer ((type), (upage), (writable), NULL, NULL) -bool vm_alloc_page_with_initializer (enum vm_type type, void *upage, - bool writable, vm_initializer *init, void *aux); -void vm_dealloc_page (struct page *page); -bool vm_claim_page (void *va); -enum vm_type page_get_type (struct page *page); - -#endif /* VM_VM_H */ + vm_alloc_page_with_initializer((type), (upage), (writable), NULL, NULL) +bool vm_alloc_page_with_initializer(enum vm_type type, void* upage, bool writable, + vm_initializer* init, void* aux); +void vm_dealloc_page(struct page* page); +bool vm_claim_page(void* va); +enum vm_type page_get_type(struct page* page); + +#endif /* VM_VM_H */ diff --git a/pintos/vm/file.c b/pintos/vm/file.c index 5f7eba9..28607a2 100644 --- a/pintos/vm/file.c +++ b/pintos/vm/file.c @@ -2,57 +2,46 @@ #include "vm/vm.h" -static bool file_backed_swap_in (struct page *page, void *kva); -static bool file_backed_swap_out (struct page *page); -static void file_backed_destroy (struct page *page); +static bool file_backed_swap_in(struct page* page, void* kva); +static bool file_backed_swap_out(struct page* page); +static void file_backed_destroy(struct page* page); /* DO NOT MODIFY this struct */ static const struct page_operations file_ops = { - .swap_in = file_backed_swap_in, - .swap_out = file_backed_swap_out, - .destroy = file_backed_destroy, - .type = VM_FILE, + .swap_in = file_backed_swap_in, + .swap_out = file_backed_swap_out, + .destroy = file_backed_destroy, + .type = VM_FILE, }; /* The initializer of file vm */ -void -vm_file_init (void) { -} +void vm_file_init(void) {} /* Initialize the file backed page */ -bool -file_backed_initializer (struct page *page, enum vm_type type, void *kva) { - /* Set up the handler */ - page->operations = &file_ops; +bool file_backed_initializer(struct page* page, enum vm_type type, void* kva) { + /* Set up the handler */ + page->operations = &file_ops; - struct file_page *file_page = &page->file; + struct file_page* file_page = &page->file; } /* Swap in the page by read contents from the file. */ -static bool -file_backed_swap_in (struct page *page, void *kva) { - struct file_page *file_page UNUSED = &page->file; +static bool file_backed_swap_in(struct page* page, void* kva) { + struct file_page* file_page UNUSED = &page->file; } /* Swap out the page by writeback contents to the file. */ -static bool -file_backed_swap_out (struct page *page) { - struct file_page *file_page UNUSED = &page->file; +static bool file_backed_swap_out(struct page* page) { + struct file_page* file_page UNUSED = &page->file; } /* Destory the file backed page. PAGE will be freed by the caller. */ -static void -file_backed_destroy (struct page *page) { - struct file_page *file_page UNUSED = &page->file; +static void file_backed_destroy(struct page* page) { + struct file_page* file_page UNUSED = &page->file; } /* Do the mmap */ -void * -do_mmap (void *addr, size_t length, int writable, - struct file *file, off_t offset) { -} +void* do_mmap(void* addr, size_t length, int writable, struct file* file, off_t offset) {} /* Do the munmap */ -void -do_munmap (void *addr) { -} +void do_munmap(void* addr) {} diff --git a/pintos/vm/vm.c b/pintos/vm/vm.c index 9b1fc4a..626990d 100644 --- a/pintos/vm/vm.c +++ b/pintos/vm/vm.c @@ -1,226 +1,261 @@ /* vm.c: Generic interface for virtual memory objects. */ +#include "vm/vm.h" + #include "threads/malloc.h" #include "threads/vaddr.h" -#include "vm/vm.h" #include "vm/inspect.h" /* Initializes the virtual memory subsystem by invoking each subsystem's * intialize codes. */ -void -vm_init (void) { - vm_anon_init (); - vm_file_init (); -#ifdef EFILESYS /* For project 4 */ - pagecache_init (); +void vm_init(void) { + vm_anon_init(); + vm_file_init(); +#ifdef EFILESYS /* For project 4 */ + pagecache_init(); #endif - register_inspect_intr (); - /* DO NOT MODIFY UPPER LINES. */ - /* TODO: Your code goes here. */ + register_inspect_intr(); + /* DO NOT MODIFY UPPER LINES. */ + /* TODO: Your code goes here. */ + + /* frame table 과 frame_lock 초가화 */ + list_init(&frame_table); + lock_init(&frame_lock); } /* Get the type of the page. This function is useful if you want to know the * type of the page after it will be initialized. * This function is fully implemented now. */ -enum vm_type -page_get_type (struct page *page) { - int ty = VM_TYPE (page->operations->type); - switch (ty) { - case VM_UNINIT: - return VM_TYPE (page->uninit.type); - default: - return ty; - } +enum vm_type page_get_type(struct page* page) { + int ty = VM_TYPE(page->operations->type); + switch (ty) { + case VM_UNINIT: + return VM_TYPE(page->uninit.type); + default: + return ty; + } } +/* static 전역 변수 */ +static struct list frame_table; +static struct lock frame_lock; + /* Helpers */ -static struct frame *vm_get_victim (void); -static bool vm_do_claim_page (struct page *page); -static struct frame *vm_evict_frame (void); -static uint64_t page_hash (const struct hash_elem *e, void *aux); -static bool page_less (const struct hash_elem *a, - const struct hash_elem *b, void *aux); +static struct frame* vm_get_victim(void); +static bool vm_do_claim_page(struct page* page); +static struct frame* vm_evict_frame(void); +static uint64_t page_hash(const struct hash_elem* e, void* aux); +static bool page_less(const struct hash_elem* a, const struct hash_elem* b, void* aux); /* Create the pending page object with initializer. If you want to create a * page, do not create it directly and make it through this function or * `vm_alloc_page`. */ -bool -vm_alloc_page_with_initializer (enum vm_type type, void *upage, bool writable, - vm_initializer *init, void *aux) { +/* 개별 페이지 생성 */ +/* 가상 주소(VA)에 해당되는 실제 page가 아닌, struct page를 조립하는 함수 */ +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; + struct page* page = NULL; // 에러 처리 시 free를 위해 초기화 + + /* 1. 이미 같은 주소(upage)에 페이지가 존재하는지 확인 */ + if (spt_find_page(spt, upage) == NULL) { + /* 2. struct page 구조체를 담을 메모리를 커널 힙에 할당 */ + page = (struct page*)malloc(sizeof(struct page)); + if (page == NULL) goto err; // 메모리 부족 등으로 할당 실패 시 + + /* 3. 타입에 맞춰 uninit 페이지로 초기화 */ + /* 이 시점에서 페이지는 'UNINIT' 상태가 되며, 나중에 접근 시 type으로 변신할 준비를 함 */ + switch (VM_TYPE(type)) { + case VM_ANON: + // anon_initializer: 나중에 실제 물리 프레임과 연결될 때 호출될 함수 + uninit_new(page, upage, init, type, aux, anon_initializer); + break; + case VM_FILE: + // file_map_initializer: 파일 기반 페이지가 생성될 때 호출될 함수 + uninit_new(page, upage, init, type, aux, file_backed_initializer); + break; + default: + goto err; // 지원하지 않는 타입 + } + + /* 4. 쓰기 권한(writable) 설정 (직접 멤버에 기록) */ + page->writable = writable; + + /* 5. 보조 페이지 테이블(SPT)에 페이지 등록 */ + if (!spt_insert_page(spt, page)) { + goto err; // 해시 테이블 삽입 실패 (충돌 등) + } + + return true; // 성공적으로 페이지 등록 완료 + } - ASSERT (VM_TYPE(type) != VM_UNINIT) - - struct supplemental_page_table *spt = &thread_current ()->spt; - - /* Check wheter the upage is already occupied or not. */ - if (spt_find_page (spt, upage) == NULL) { - /* TODO: Create the page, fetch the initialier according to the VM type, - * TODO: and then create "uninit" page struct by calling uninit_new. You - * TODO: should modify the field after calling the uninit_new. */ - - /* TODO: Insert the page into the spt. */ - } err: - return false; + /* 실패 시, 만약 page 구조체가 할당되었다면 해제하여 메모리 누수 방지 */ + if (page) free(page); + return false; } /* Find VA from spt and return page. On error, return NULL. */ -struct page * -spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) { - /* TODO: Fill this function. */ - struct page dummy_page; - struct hash_elem *elem; +struct page* spt_find_page(struct supplemental_page_table* spt UNUSED, void* va UNUSED) { + /* TODO: Fill this function. */ + struct page dummy_page; + struct hash_elem* elem; - if (spt == NULL || va == NULL) - return NULL; + if (spt == NULL || va == NULL) return NULL; - dummy_page.va = pg_round_down (va); - elem = hash_find (&spt->hash_table, &dummy_page.hash_elem); + dummy_page.va = pg_round_down(va); + elem = hash_find(&spt->hash_table, &dummy_page.hash_elem); - if (elem == NULL) - return NULL; - - return hash_entry(elem, struct page, hash_elem); + if (elem == NULL) return NULL; + + return hash_entry(elem, struct page, hash_elem); } /* Insert PAGE into spt with validation. */ -bool spt_insert_page (struct supplemental_page_table *spt, struct page *page) { - if (spt == NULL || page == NULL || page->va == NULL) - return false; +bool spt_insert_page(struct supplemental_page_table* spt, struct page* page) { + if (spt == NULL || page == NULL || page->va == NULL) return false; - // page->va = pg_round_down (page->va); - return hash_insert (&spt->hash_table, &page->hash_elem) == NULL; + // page->va = pg_round_down (page->va); + return hash_insert(&spt->hash_table, &page->hash_elem) == NULL; } -bool spt_remove_page (struct supplemental_page_table *spt, struct page *page) { - struct hash_elem *result; +bool spt_remove_page(struct supplemental_page_table* spt, struct page* page) { + struct hash_elem* result; - if (spt == NULL || page == NULL) - return; + if (spt == NULL || page == NULL) return; - result = hash_delete(&spt->hash_table, &page->hash_elem); + result = hash_delete(&spt->hash_table, &page->hash_elem); - if (result != NULL) { - vm_dealloc_page (page); - return true; - } - return false; + if (result != NULL) { + vm_dealloc_page(page); + return true; + } + return false; } /* Get the struct frame, that will be evicted. */ -static struct frame * -vm_get_victim (void) { - struct frame *victim = NULL; - /* TODO: The policy for eviction is up to you. */ +static struct frame* vm_get_victim(void) { + struct frame* victim = NULL; + /* TODO: The policy for eviction is up to you. */ - return victim; + return victim; } /* Evict one page and return the corresponding frame. * Return NULL on error.*/ -static struct frame * -vm_evict_frame (void) { - struct frame *victim UNUSED = vm_get_victim (); - /* TODO: swap out the victim and return the evicted frame. */ +static struct frame* vm_evict_frame(void) { + struct frame* victim UNUSED = vm_get_victim(); + /* TODO: swap out the victim and return the evicted frame. */ - return NULL; + return NULL; } /* palloc() and get frame. If there is no available page, evict the page * and return it. This always return valid address. That is, if the user pool * memory is full, this function evicts the frame to get the available memory * space.*/ -static struct frame * -vm_get_frame (void) { - struct frame *frame = NULL; - /* TODO: Fill this function. */ +static struct frame* vm_get_frame(void) { + struct frame* frame = NULL; + + void* kva = palloc_get_page(PAL_USER | PAL_ZERO); + if (kva == NULL) { + PANIC("todo"); + } - ASSERT (frame != NULL); - ASSERT (frame->page == NULL); - return frame; + frame = (struct frame*)malloc(sizeof(struct frame)); + if (frame == NULL) PANIC("Frame Malloc Failed"); + + frame->kva = kva; + frame->page = NULL; + + lock_acquire(&frame_lock); + list_push_back(&frame_table, &frame->frame_elem); + lock_release(&frame_lock); + + return frame; } /* Growing the stack. */ -static void -vm_stack_growth (void *addr UNUSED) { -} +static void vm_stack_growth(void* addr UNUSED) {} /* Handle the fault on write_protected page */ -static bool -vm_handle_wp (struct page *page UNUSED) { -} +static bool vm_handle_wp(struct page* page UNUSED) {} /* Return true on success */ -bool -vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED, - bool user UNUSED, bool write UNUSED, bool not_present UNUSED) { - struct supplemental_page_table *spt UNUSED = &thread_current ()->spt; - struct page *page = NULL; - /* TODO: Validate the fault */ - /* TODO: Your code goes here */ - - return vm_do_claim_page (page); +bool vm_try_handle_fault(struct intr_frame* f UNUSED, void* addr UNUSED, bool user UNUSED, + bool write UNUSED, bool not_present UNUSED) { + struct supplemental_page_table* spt UNUSED = &thread_current()->spt; + struct page* page = NULL; + /* TODO: Validate the fault */ + /* TODO: Your code goes here */ + + return vm_do_claim_page(page); } /* Free the page. * DO NOT MODIFY THIS FUNCTION. */ -void -vm_dealloc_page (struct page *page) { - destroy (page); - free (page); +void vm_dealloc_page(struct page* page) { + destroy(page); + free(page); } /* Claim the page that allocate on VA. */ -bool -vm_claim_page (void *va UNUSED) { - struct page *page = NULL; - /* TODO: Fill this function */ +bool vm_claim_page(void* va UNUSED) { + struct page* page = NULL; + /* TODO: Fill this function */ - return vm_do_claim_page (page); + return vm_do_claim_page(page); } /* Claim the PAGE and set up the mmu. */ -static bool -vm_do_claim_page (struct page *page) { - struct frame *frame = vm_get_frame (); - - /* Set links */ - 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); +static bool vm_do_claim_page(struct page* page) { + // vm_get_frame으로 + struct frame* frame = vm_get_frame(); + if (frame == NULL) return false; + + /* Set links */ + frame->page = page; + page->frame = frame; + + /* TODO: Insert page table entry to map page's VA to frame's PA. */ + /* 3. Physical Memory의 User Pool에 frame과 + Virtual Address Space의 User Space에 page의 1 : 1 매핑 */ + struct thread* cur = thread_current(); + if (!pml4_set_page(cur->pml4, page->va, frame->kva, page->writable)) return false; + + /* 3. 데이터 로딩 (Swap In) */ + // 현재는 Anonymous Page(Stack/Heap) 위주이므로 true 반환으로 충분하지만, + // 추후 File Loading 등을 위해 swap_in 구조를 살리는 것이 정석입니다. + // return swap_in(page, frame->kva); + return true; } /* Initialize new supplemental page table */ -void -supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) { - hash_init(&spt->hash_table, page_hash, page_less, NULL); +void supplemental_page_table_init(struct supplemental_page_table* spt UNUSED) { + hash_init(&spt->hash_table, page_hash, page_less, NULL); } /* Copy supplemental page table from src to dst */ -bool -supplemental_page_table_copy (struct supplemental_page_table *dst UNUSED, - struct supplemental_page_table *src UNUSED) { -} +bool supplemental_page_table_copy(struct supplemental_page_table* dst UNUSED, + struct supplemental_page_table* src UNUSED) {} /* Free the resource hold by the supplemental page table */ -void -supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED) { - /* TODO: Destroy all the supplemental_page_table hold by thread and - * TODO: writeback all the modified contents to the storage. */ +void supplemental_page_table_kill(struct supplemental_page_table* spt UNUSED) { + /* TODO: Destroy all the supplemental_page_table hold by thread and + * TODO: writeback all the modified contents to the storage. */ } -static uint64_t page_hash (const struct hash_elem *e, void *aux UNUSED) { - const struct page *page; - page = hash_entry (e, struct page, hash_elem); - return hash_bytes (&page->va, sizeof page->va); +static uint64_t page_hash(const struct hash_elem* e, void* aux UNUSED) { + const struct page* page; + page = hash_entry(e, struct page, hash_elem); + return hash_bytes(&page->va, sizeof page->va); } -static bool page_less (const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) { - const struct page *page_a, *page_b; - page_a = hash_entry (a, struct page, hash_elem); - page_b = hash_entry (b, struct page, hash_elem); - return page_a->va < page_b->va; +static bool page_less(const struct hash_elem* a, const struct hash_elem* b, void* aux UNUSED) { + const struct page *page_a, *page_b; + page_a = hash_entry(a, struct page, hash_elem); + page_b = hash_entry(b, struct page, hash_elem); + return page_a->va < page_b->va; }