-
[PintOS] WIL (4) 난 이해를 한 걸까..?SW 정글/운영체제 2024. 10. 22. 00:09
이 글은 swap disk와 copy on write에 대해 무지성으로 작성되었습니다.
Copy On Write
현재까지는 fork할 때 메모리가 할당된 page에 대해서 부모에서 자식으로 복사할 때 같은 내용의 물리메모리를 자식에게도 할당 해주었다. 같은 메모리가 두 번 복사되기에 메모리 낭비로 볼 수도 있다.cow는 fork시에는 부모와 자식이 동일한 물리 메모리를 가리키게 하고
해당 페이지에 write 요청이 발생해야 새로운 물리메모리를 할당해 주도록 수정한다.
fork 시 물리메모리를 모두 복사하지 않고 부모와 같은 물리메모리를 공유하다가 write작업 시 해당 페이지의 물리메모리를 새로 맵핑한다.
부모와 자식 프로세스는 쓰기 작업 이후에 서로 다른 메모리 프레임을 가지게 되어, 쓰기 작업이 서로에게 영향을 주지 않
** 페이지를 만들 때부터 쓰려고 하면 page fault가 나도록 해야 함
여기부터는 수정이 있었던 파일 일부에 대해 간략히 메모하고 지나가겠다.
1. thread.h 수정
#ifdef VM /* Table for whole virtual memory owned by thread. */ struct supplemental_page_table spt; void *rsp; void* stack_bottom; #endif
- 각 스레드마다 보조 페이지 테이블(supplemental_page_table)을 추가하여 가상 메모리를 관리
- rsp는 현재 스레드의 스택 포인터를 저장하며, 커널 모드와 유저 모드 전환 시 사용
- stack_bottom은 현재 스레드의 스택의 바닥 주소를 나타내며, 스택 확장 시 사용
2. anon.h
#ifndef VM_ANON_H #define VM_ANON_H #include "vm/vm.h" #define SECTOR_SIZE 512 // 추가된 부분 struct page; enum vm_type; struct anon_page { int bit_idx; // 추가된 부분: 스왑 공간에서의 비트맵 인덱스를 저장 }; void vm_anon_init (void);
- SECTOR_SIZE를 정의하여 디스크 섹터의 크기를 지정
- anon_page 구조체에 bit_idx를 추가하여 페이지가 스왑 디스크의 어느 위치에 저장되었는지 추적
- 이는 스왑 인/아웃 시 해당 페이지의 위치를 알아내기 위해 필요
3. file.h
enum vm_type; struct file_page { struct file *file; off_t ofs; void *file_start_page; void *file_end_page; size_t page_read_bytes; size_t page_zero_bytes; }; void vm_file_init (void);
file_page 구조체를 정의하여 파일 기반 페이지에 필요한 정보를 저장
- file: 해당 페이지가 속한 파일을 가리킨다.
- ofs: 파일 내에서의 오프셋
- file_start_page, file_end_page: 메모리 매핑된 파일의 시작과 끝 페이지 주소
- page_read_bytes: 페이지에서 읽어야 할 바이트 수
- page_zero_bytes: 페이지에서 0으로 채워야 할 바이트 수
4. vm.h (1)
struct frame { void *kva; struct page *page; struct list_elem frame_elem; // 추가된 부분: 프레임 테이블 관리를 위한 리스트 요소 }; struct page { struct frame *frame; /* Back reference for frame */ /* Your implementation */ struct hash_elem page_elem; /* 해시 테이블 요소 */ bool is_writable; int bit_idx; // 스왑 비트맵 인덱스 union { struct uninit_page uninit; struct anon_page anon; struct file_page file; #ifdef EFILESYS struct page_cache page_cache; #endif }; };
- frame 구조체에 frame_elem을 추가하여 프레임 테이블을 리스트로 관리
- page 구조체에 page_elem을 추가하여 보조 페이지 테이블에서 페이지를 해시 테이블로 관리
- is_writable: 페이지의 쓰기 가능 여부를 나타냄
- bit_idx: 스왑 공간에서의 인덱스를 저장하여 스왑 인/아웃 시 사용
5. vm.h(2)
// for aux struct load_info{ struct file *file; size_t page_read_bytes; size_t page_zero_bytes; off_t ofs; bool writable; size_t file_start_page; size_t file_end_page; };
- load_info 구조체를 정의하여 지연 로딩(lazy loading) 시 필요한 정보를 저장
- aux로 전달되어 페이지를 초기화할 때 사용
6. process.c (1)
int process_wait(tid_t child_tid UNUSED) { struct thread *child = get_child_process(child_tid); if (child == NULL) return -1; sema_down(&child->wait_sema); list_remove(&child->child_elem); int result = child->exit_status; sema_up(&child->exit_sema); return result; // 변경된 부분: result를 반환 }
- process_wait 함수에서 child->exit_status 대신 result 변수를 반환하도록 수정
- 이는 child->exit_status가 sema_up 이후에 변경될 수 있으므로, 미리 값을 저장해 두기 위함
7. process.c (2)
void process_exit(void) { struct thread *cur = thread_current(); // FDT의 모든 파일을 닫는다. for (int i = 2; i < FDT_COUNT_LIMIT; i++) { process_close_file(i); } palloc_free_multiple(cur->fdt, FDT_PAGES); // 현재 실행 중인 파일도 닫는다. file_close(cur->running); process_cleanup(); // 부모에게 종료를 알린다. sema_up(&cur->wait_sema); // 부모의 signal을 기다린다. sema_down(&cur->exit_sema); // 추가된 부분: 보조 페이지 테이블 해시 제거 hash_destroy(&cur->spt.pages, page_dealloc); }
- 프로세스가 종료될 때 보조 페이지 테이블을 제거하여 메모리 누수를 방지
- hash_destroy와 page_dealloc을 사용하여 페이지를 해제
8. lazy_load_segment에서 file seek 추가 및 aux 사용
struct load_info *info = (struct load_info *)aux; uint8_t *kpage = page->frame->kva; file_seek(info->file, info->ofs); if (file_read(info->file, kpage, info->page_read_bytes) != (int)info->page_read_bytes){ palloc_free_page(kpage); free(aux); return false; } memset(kpage + info->page_read_bytes, 0, info->page_zero_bytes); free(aux); return true;
- lazy_load_segment 함수에서 file_seek을 사용하여 파일의 정확한 위치에서 읽음
- aux로 전달된 load_info 구조체를 사용하여 필요한 정보를 가져옴
- 페이지를 메모리에 로드한 후 aux를 해제
9. load_segment 함수 수정
bool load_segment(struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable) { ASSERT((read_bytes + zero_bytes) % PGSIZE == 0); ASSERT(pg_ofs(upage) == 0); ASSERT(ofs % PGSIZE == 0); file_seek (file, ofs); while (read_bytes > 0 || zero_bytes > 0) { size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; size_t page_zero_bytes = PGSIZE - page_read_bytes; struct load_info *aux = (struct load_info *)malloc(sizeof(struct load_info)); if(aux == NULL) return false; memset(aux, 0, sizeof(struct load_info)); aux->file = file; aux->page_read_bytes = page_read_bytes; aux->page_zero_bytes = page_zero_bytes; aux->writable = writable; aux->ofs = ofs; if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable, lazy_load_segment, aux)) return false; read_bytes -= page_read_bytes; zero_bytes -= page_zero_bytes; upage += PGSIZE; ofs += page_read_bytes; } return true; }
- aux로 load_info 구조체를 만들어 지연 로딩에 필요한 정보를 전달
- file_seek을 통해 파일의 오프셋을 설정
- vm_alloc_page_with_initializer를 사용하여 페이지를 할당하고 초기화
10. setup_stack 함수 수정
static bool setup_stack(struct intr_frame *if_) { bool success = false; void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE); if(vm_alloc_page(VM_ANON | VM_MARKER_0, stack_bottom, 1)){ success = vm_claim_page(stack_bottom); if(success){ if_->rsp = USER_STACK; thread_current()->stack_bottom = stack_bottom; } } return success; }
- 스택을 초기화할 때 페이지를 할당하고 즉시 클레임(claim)
- stack_bottom을 thread_current()에 저장하여 이후 스택 확장 시 사용
- 유저 스택의 최상단 주소를 if_->rsp에 설정
11. anon.c에서 swap in/out 구현
static bool anon_swap_in (struct page *page, void *kva) { struct anon_page *anon_page = &page->anon; int idx = anon_page->bit_idx; if(idx < 0) { return false; } for (int i = 0; i < 8; i++) { disk_sector_t sec_no = (disk_sector_t) idx*8 + i; disk_read(swap_disk, sec_no, kva + SECTOR_SIZE * i); } anon_page->bit_idx = -1; bitmap_flip(swap_table.swap_used_map, idx); return true; } static bool anon_swap_out (struct page *page) { struct anon_page *anon_page = &page->anon; struct frame *victim_frame = page->frame; int idx = bitmap_scan(swap_table.swap_used_map, 0, 1, false); if (idx == BITMAP_ERROR) { return false; } for (int i = 0; i < 8; i++) { disk_sector_t sec_no = (disk_sector_t) idx*8 + i; disk_write(swap_disk, sec_no, (victim_frame->kva) + SECTOR_SIZE * i); } anon_page->bit_idx = idx; pml4_clear_page(thread_current()->pml4, page->va); bitmap_flip(swap_table.swap_used_map, idx); return true; } static void anon_destroy (struct page *page) { struct anon_page *anon_page = &page->anon; struct thread *cur = thread_current(); hash_delete(&cur->spt.pages, &page->page_elem); if (page->frame){ pml4_clear_page(cur->pml4, page->va); palloc_free_page(page->frame->kva); list_remove(&page->frame->frame_elem); free(page->frame); page->frame = NULL; } }
- anon_swap_in: 스왑 디스크에서 페이지를 메모리로 읽어오기
- 스왑 공간에서 bit_idx를 사용하여 위치를 찾기
- 8개의 섹터를 읽어와 페이지 크기(4KB)를 채우기
- 스왑 비트맵을 업데이트
- anon_swap_out: 메모리의 페이지를 스왑 디스크로 내보내기
- 스왑 비트맵에서 빈 공간을 찾기
- 8개의 섹터에 페이지를 기록
- 페이지 테이블 엔트리를 제거
- anon_destroy: 페이지를 해제하고 관련 자원을 정리
12. file.c에서 파일 기반 페이지의 swap in/out 구현
bool file_backed_initializer (struct page *page, enum vm_type type, void *kva) { page->operations = &file_ops; struct file_page *file_page = &page->file; return true; } static bool file_backed_swap_in (struct page *page, void *kva) { struct file_page *file_page = &page->file; struct file *file = file_page->file; file_seek(file, file_page->ofs); file_read_at(file, kva, file_page->page_read_bytes, file_page->ofs); return true; } static bool file_backed_swap_out (struct page *page) { uint64_t *pml4 = thread_current()->pml4; struct file_page *file_page = &page->file; if(pml4_is_dirty(pml4, page->va)){ struct file *file = file_page->file; file_seek(file, file_page->ofs); file_write_at(file, page->frame->kva, file_page->page_read_bytes, file_page->ofs); pml4_set_dirty(pml4, page->va, false); } pml4_clear_page(pml4, page->va); return true; } static void file_backed_destroy (struct page *page) { struct file_page *file_page = &page->file; uint64_t *pml4 = thread_current()->pml4; struct supplemental_page_table *spt = &thread_current()->spt; hash_delete(&spt->pages, &page->page_elem); if(page->frame != NULL){ if(pml4_is_dirty(pml4, page->va)) file_write_at(file_page->file, page->va, file_page->page_read_bytes, file_page->ofs); pml4_clear_page(pml4, page->va); palloc_free_page(page->frame->kva); list_remove(&page->frame->frame_elem); free(page->frame); page->frame = NULL; } }
- file_backed_swap_in: 파일에서 페이지를 읽어와 메모리에 로드
- file_backed_swap_out: 페이지가 수정되었는지 확인하고, 수정되었으면 파일에 내용을 기록
- file_backed_destroy: 페이지를 해제하고, 수정된 내용이 있으면 파일에 반영
vm.c 코드 (전체코드)
더보기더보기/* vm.c: Generic interface for virtual memory objects. */ #include "threads/malloc.h" #include "vm/vm.h" #include "vm/inspect.h" #include "threads/mmu.h" #include "userprog/process.h" #include "userprog/syscall.h" #include <string.h> static unsigned page_hash (const struct hash_elem *p_, void *aux UNUSED); static bool page_less (const struct hash_elem *a_, const struct hash_elem *b_, void *aux UNUSED); static struct page *page_lookup (struct supplemental_page_table *spt, const void *address); struct list frame_table; struct lock frame_table_lock; /* Initializes the virtual memory subsystem by invoking each subsystem's * intialize codes. */ void vm_init (void) { vm_anon_init (); vm_file_init (); list_init(&frame_table); lock_init(&frame_table_lock); #ifdef EFILESYS /* For project 4 */ pagecache_init (); #endif register_inspect_intr (); /* DO NOT MODIFY UPPER LINES. */ /* TODO: Your code goes here. */ } /* 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; } } /* Helpers */ static struct frame *vm_get_victim (void); static bool vm_do_claim_page (struct page *page); static struct frame *vm_evict_frame (void); /* 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) { ASSERT (VM_TYPE(type) != VM_UNINIT) struct supplemental_page_table *spt = &thread_current ()->spt; struct page* page = spt_find_page (spt, upage); /* Check wheter the upage is already occupied or not. */ if (page == 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. */ page = malloc(sizeof(struct page)); if (page == NULL) goto err; bool (*initializer)(struct page *, enum vm_type, void *); switch (VM_TYPE(type)){ case VM_ANON: initializer = anon_initializer; break; case VM_FILE: initializer = file_backed_initializer; break; default: goto err; } uninit_new(page, upage, init, type, aux, initializer); page->is_writable = writable; /* TODO: Insert the page into the spt. */ return spt_insert_page(spt, page); } err: 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) { struct page *page = NULL; /* TODO: Fill this function. */ return page_lookup(spt, va); } /* Insert PAGE into spt with validation. */ bool spt_insert_page (struct supplemental_page_table *spt UNUSED, struct page *page UNUSED) { int succ = false; /* TODO: Fill this function. */ struct hash_elem * result = hash_insert(&spt->pages, &page->page_elem); if(result == NULL) succ = true; return succ; } void spt_remove_page (struct supplemental_page_table *spt, struct page *page) { vm_dealloc_page (page); return true; } /* Get the struct frame, that will be evicted. */ static struct frame * vm_get_victim (void) { /* TODO: The policy for eviction is up to you. */ struct list_elem *e = list_pop_front(&frame_table); struct frame *victim = list_entry(e, struct frame, frame_elem); return victim; } /* Evict one page and return the corresponding frame. * Return NULL on error.*/ static struct frame * vm_evict_frame (void) { struct frame *victim = vm_get_victim (); /* TODO: swap out the victim and return the evicted frame. */ struct page *victim_page = victim->page; if(swap_out(victim_page)) { victim_page->frame = NULL; victim->page = NULL; memset(victim->kva, 0, PGSIZE); return victim; } 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 = (struct frame*)malloc(sizeof(struct frame)); ASSERT (frame != NULL); /* TODO: Fill this function. */ frame->kva = palloc_get_page(PAL_USER); frame->page = NULL; if(frame->kva == NULL) { free(frame); frame = vm_evict_frame(); } ASSERT (frame->page == NULL); lock_acquire(&frame_table_lock); list_push_back(&frame_table, &frame->frame_elem); lock_release(&frame_table_lock); return frame; } /* Growing the stack. */ static void vm_stack_growth (void *addr UNUSED) { if(vm_alloc_page(VM_ANON | VM_MARKER_0, pg_round_down(addr), 1)) thread_current()->stack_bottom -= PGSIZE; } /* Handle the fault on write_protected page */ static bool vm_handle_wp (struct page *page UNUSED) { } /* Return true on success */ 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; /* TODO: Validate the fault */ if(addr == NULL || is_kernel_vaddr(addr)){ return false; } if (!not_present){ return false; } void *rsp = f->rsp; // user access인 경우 rsp는 유저 stack을 가리킨다. if (!user) // kernel access인 경우 thread에서 rsp를 가져와야 한다. rsp = thread_current()->rsp; // 스택 확장으로 처리할 수 있는 폴트인 경우, vm_stack_growth를 호출한다. if (rsp-8 <= addr && USER_STACK - 0x100000 <= addr && addr <= USER_STACK) vm_stack_growth(pg_round_down(addr)); struct page *page = spt_find_page(spt, addr); if (page == NULL){ return false; } if (write && !page->is_writable){ return false; } /* TODO: Your code goes here */ bool success = vm_do_claim_page (page); return success; } /* Free the page. * DO NOT MODIFY THIS FUNCTION. */ 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 thread *cur = thread_current(); struct page *page = spt_find_page(&cur->spt, va); /* TODO: Fill this function */ if (page == NULL){ return false; } 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. */ struct thread *cur = thread_current(); pml4_set_page(cur->pml4, page->va, frame->kva, page->is_writable); return swap_in(page, frame->kva); } /* Initialize new supplemental page table */ void supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) { hash_init (&spt->pages, page_hash, page_less, NULL); } /* Returns a hash value for page p. */ unsigned page_hash (const struct hash_elem *p_, void *aux UNUSED) { const struct page *p = hash_entry (p_, struct page, page_elem); return hash_bytes (&p->va, sizeof p->va); } /* Returns true if page a precedes page b. */ bool page_less (const struct hash_elem *a_, const struct hash_elem *b_, void *aux UNUSED) { const struct page *a = hash_entry (a_, struct page, page_elem); const struct page *b = hash_entry (b_, struct page, page_elem); return a->va < b->va; } /* Returns the page containing the given virtual address, or a null pointer if no such page exists. */ struct page * page_lookup (struct supplemental_page_table *spt, const void *va) { struct page p; struct hash_elem *e; p.va = pg_round_down(va); e = hash_find (&spt->pages, &p.page_elem); return e != NULL ? hash_entry (e, struct page, page_elem) : NULL; } /* Copy supplemental page table from src to dst */ bool supplemental_page_table_copy (struct supplemental_page_table *dst, struct supplemental_page_table *src) { struct hash_iterator i; struct hash_elem *elem; hash_first(&i, &src->pages); while ((elem = hash_next(&i))){ struct page *p = hash_entry(elem, struct page, page_elem); enum vm_type type = page_get_type(p); if (VM_TYPE(p->operations->type) == VM_UNINIT){ //if(!vm_alloc_page_with_initializer(VM_ANON, p->va, p->is_writable, p->uninit.init, p->uninit.aux)) // 왜 ANON? struct load_info *copy_aux = (struct load_info *)malloc(sizeof(struct load_info)); memcpy(copy_aux, p->uninit.aux, sizeof(struct load_info)); if(!vm_alloc_page_with_initializer(type, p->va, p->is_writable, p->uninit.init, copy_aux)) return false; }else{ if(vm_alloc_page(type, p->va, p->is_writable) && vm_claim_page(p->va)){ struct page* copy = spt_find_page(dst, p->va); memcpy(copy->frame->kva, p->frame->kva, PGSIZE); copy->frame->page = copy; }else return false; } } return true; } /* 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. */ // hash_destroy(&spt->pages, page_dealloc); hash_clear(&spt->pages, page_dealloc); } void page_dealloc(struct hash_elem *e, void *aux UNUSED) { struct page *target = hash_entry (e, struct page, page_elem); destroy(target); free(target); }
핀토스의 vm.c 파일은 가상 메모리 객체에 대한 일반적인 인터페이스를 구현한다.
이 코드는 주로 가상 메모리의 초기화, 페이지 할당, 페이지 타입 확인, 페이지 테이블 관리, 페이지 폴트 처리, 스왑 인/아웃, 스택 확장 등의 기능을 포함한다.
1.초기화 함수 (vm_init):
- 가상 메모리 서브시스템 초기화를 담당한다.
- vm_anon_init(), vm_file_init() 등 서브시스템 초기화를 호출하고, 프레임 테이블을 초기화하며, 락을 설정한다.
void vm_init (void) { vm_anon_init (); vm_file_init (); list_init(&frame_table); lock_init(&frame_table_lock); #ifdef EFILESYS /* For project 4 */ pagecache_init (); #endif register_inspect_intr (); /* DO NOT MODIFY UPPER LINES. */ /* TODO: Your code goes here. */ }
2. 페이지 타입 가져오기 (page_get_type):
- 페이지의 타입을 확인하는 함수로, 페이지가 어떤 타입(익명 페이지, 파일 백업 페이지 등)인지 반환한다.
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; } }
3. 페이지 할당 (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; struct page* page = spt_find_page (spt, upage); /* Check wheter the upage is already occupied or not. */ if (page == 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. */ page = malloc(sizeof(struct page)); if (page == NULL) goto err; bool (*initializer)(struct page *, enum vm_type, void *); switch (VM_TYPE(type)){ case VM_ANON: initializer = anon_initializer; break; case VM_FILE: initializer = file_backed_initializer; break; default: goto err; } uninit_new(page, upage, init, type, aux, initializer); page->is_writable = writable; /* TODO: Insert the page into the spt. */ return spt_insert_page(spt, page); } err: return false; }
4. 페이지 찾기 및 삽입 (spt_find_page, spt_insert_page):
- 주소를 사용하여 보조 페이지 테이블에서 해당 페이지를 찾거나, 새 페이지를 삽입한다.
struct page * spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) { struct page *page = NULL; /* TODO: Fill this function. */ return page_lookup(spt, va); }
bool spt_insert_page (struct supplemental_page_table *spt UNUSED, struct page *page UNUSED) { int succ = false; /* TODO: Fill this function. */ struct hash_elem * result = hash_insert(&spt->pages, &page->page_elem); if(result == NULL) succ = true; return succ; }
5. 페이지 제거 (spt_remove_page):
- 주어진 페이지를 보조 페이지 테이블에서 제거하고 메모리에서 할당 해제한다.
void spt_remove_page (struct supplemental_page_table *spt, struct page *page) { vm_dealloc_page (page); return true; }
6. 페이지 스왑 아웃 및 프레임 희생자 선택 (vm_evict_frame, vm_get_victim):
- 메모리에서 프레임을 제거(스왑 아웃)하고, 스왑 아웃할 프레임의 희생자를 결정한다.
static struct frame * vm_get_victim (void) { /* TODO: The policy for eviction is up to you. */ struct list_elem *e = list_pop_front(&frame_table); struct frame *victim = list_entry(e, struct frame, frame_elem); return victim; } /* Evict one page and return the corresponding frame. * Return NULL on error.*/ static struct frame * vm_evict_frame (void) { struct frame *victim = vm_get_victim (); /* TODO: swap out the victim and return the evicted frame. */ struct page *victim_page = victim->page; if(swap_out(victim_page)) { victim_page->frame = NULL; victim->page = NULL; memset(victim->kva, 0, PGSIZE); return victim; } return NULL; }
7. 스택 확장 (vm_stack_growth):
- 주소가 주어지면 스택을 확장하는 기능을 제공한다. 주소가 스택 확장 가능 범위 내에 있는지 확인 후 확장을 수행한다.
static void vm_stack_growth (void *addr UNUSED) { if(vm_alloc_page(VM_ANON | VM_MARKER_0, pg_round_down(addr), 1)) thread_current()->stack_bottom -= PGSIZE; }
8. 페이지 폴트 처리 (vm_try_handle_fault):
- 페이지 폴트가 발생했을 때, 해당 주소에 대한 페이지를 찾고, 필요에 따라 페이지를 로드하거나 스택을 확장하는 로직을 포함한다.
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; /* TODO: Validate the fault */ if(addr == NULL || is_kernel_vaddr(addr)){ return false; } if (!not_present){ return false; } void *rsp = f->rsp; // user access인 경우 rsp는 유저 stack을 가리킨다. if (!user) // kernel access인 경우 thread에서 rsp를 가져와야 한다. rsp = thread_current()->rsp; // 스택 확장으로 처리할 수 있는 폴트인 경우, vm_stack_growth를 호출한다. if (rsp-8 <= addr && USER_STACK - 0x100000 <= addr && addr <= USER_STACK) vm_stack_growth(pg_round_down(addr)); struct page *page = spt_find_page(spt, addr); if (page == NULL){ return false; } if (write && !page->is_writable){ return false; } /* TODO: Your code goes here */ bool success = vm_do_claim_page (page); return success; }
9. 페이지 및 프레임 클레임 (vm_claim_page, vm_do_claim_page):
- 주소에 해당하는 페이지를 클레임하고, 물리 메모리에 매핑한다.
bool vm_claim_page (void *va UNUSED) { struct thread *cur = thread_current(); struct page *page = spt_find_page(&cur->spt, va); /* TODO: Fill this function */ if (page == NULL){ return false; } 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. */ struct thread *cur = thread_current(); pml4_set_page(cur->pml4, page->va, frame->kva, page->is_writable); return swap_in(page, frame->kva); }
10. 보조 페이지 테이블 관련 함수 (supplemental_page_table_init, supplemental_page_table_copy, supplemental_page_table_kill):
- 보조 페이지 테이블을 초기화하거나, 복사하거나, 종료 처리를 담당한다.
void supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) { hash_init (&spt->pages, page_hash, page_less, NULL); }
bool supplemental_page_table_copy (struct supplemental_page_table *dst, struct supplemental_page_table *src) { struct hash_iterator i; struct hash_elem *elem; hash_first(&i, &src->pages); while ((elem = hash_next(&i))){ struct page *p = hash_entry(elem, struct page, page_elem); enum vm_type type = page_get_type(p); if (VM_TYPE(p->operations->type) == VM_UNINIT){ //if(!vm_alloc_page_with_initializer(VM_ANON, p->va, p->is_writable, p->uninit.init, p->uninit.aux)) // 왜 ANON? struct load_info *copy_aux = (struct load_info *)malloc(sizeof(struct load_info)); memcpy(copy_aux, p->uninit.aux, sizeof(struct load_info)); if(!vm_alloc_page_with_initializer(type, p->va, p->is_writable, p->uninit.init, copy_aux)) return false; }else{ if(vm_alloc_page(type, p->va, p->is_writable) && vm_claim_page(p->va)){ struct page* copy = spt_find_page(dst, p->va); memcpy(copy->frame->kva, p->frame->kva, PGSIZE); copy->frame->page = copy; }else return false; } } return true; }
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. */ // hash_destroy(&spt->pages, page_dealloc); hash_clear(&spt->pages, page_dealloc); }
'SW 정글 > 운영체제' 카테고리의 다른 글
[Sorting] 56. Merge Intervals (0) 2024.10.24 [PintOS] WIL (3) Lazy load segment / 지연 로딩 간단한 수준으로만 (0) 2024.10.14 [PintOS] WIL (2) Argument_Passing / System_Call (수정전) (0) 2024.10.08 [PintOS] WIL (1) alarm-clock 위주 (추후 보완 예정) (2) 2024.10.01