[Pintos-KAIST] Project 3 : Copy On Write

์œ ์„ ยท2024๋…„ 5์›” 27์ผ
4

Pintos - KAIST

๋ชฉ๋ก ๋ณด๊ธฐ
16/16
post-thumbnail

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป GITHUB ๋ ˆํฌ์ง€ํ† ๋ฆฌ
๐Ÿ‘ฉ๐Ÿปโ€๐Ÿ’ป GITHUB Copy On Write ์ด์Šˆ

๊ณผ์ œ ์„ค๋ช…

Copy-on-write๋Š” ๋™์ผํ•œ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ด์šฉํ•˜์—ฌ ๋” ๋น ๋ฅธ ๋ณต์ œ ์ž‘์—…์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ ๊ธฐ์ˆ ์ด๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ, ์–ด๋–ค ๋ฆฌ์†Œ์Šค๊ฐ€ ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค์— ์˜ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ, ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๊ฐ ํ”„๋กœ์„ธ์Šค์—๋Š” ํ•ด๋‹น ๋ฆฌ์†Œ์Šค์˜ ๊ณ ์œ ํ•œ ๋ณต์‚ฌ๋ณธ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ˆ˜์ •๋˜์ง€ ์•Š๊ณ  ์ฝ๊ธฐ๋งŒ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ์— ํ•ด๋‹น ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ณต์‚ฌ๋ณธ์ด ์žˆ์„ ํ•„์š”๊ฐ€ ์—†๋‹ค.

๊ทธ๋ž˜์„œ fork๋ฅผ ํ•  ๋•Œ์—๋Š” ์ƒˆ๋กœ์šด ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•˜์ง€ ์•Š๊ณ  ๋ถ€๋ชจ์™€ ๊ฐ™์€ ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ๋งคํ•‘๋งŒ ํ•ด์ฃผ์—ˆ๋‹ค๊ฐ€ ์ถ”ํ›„์— write๋ฅผ ์‹œ๋„ํ•  ๋•Œ์—๋งŒ ์ƒˆ๋กœ์šด ๋ฌผ๋ฆฌ ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.


๊ตฌํ˜„

1. page ๊ตฌ์กฐ์ฒด์— accessible ์ถ”๊ฐ€

  • include/vm/vm.h
  • ๋ถ€๋ชจ์™€ ๊ฐ™์€ page์— writeํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ฒƒ์ธ์ง€ ์•„๋‹ˆ๋ฉด ์ง„์งœ๋กœ ์ž‘์„ฑํ•˜๋ฉด ์•ˆ๋˜๋Š” ํŽ˜์ด์ง€๋ฅผ ์š”์ฒญํ•œ ๊ฒƒ์ธ์ง€ ๊ตฌ๋ถ„ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์ƒˆ๋กœ์šด accessible ๊ตฌ์กฐ์ฒด ์ถ”๊ฐ€
strutc page {
...
	/** Project 3-Memory Management */
	struct hash_elem hash_elem;
	bool writable;
	/** Project 3-Copy On Write */
	bool accessible;
};

2. supplemental_page_table_copy() ์ˆ˜์ •

  • vm/vm.c
  • ๊ธฐ์กด์—๋Š” anonymous page๋ฅผ forkํ•  ๋•Œ ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ณ  kva ๋˜ํ•œ ํ• ๋‹นํ•˜์—ฌ ๋ฌผ๋ฆฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์ œํ•˜๋Š” ๋ฐฉ์‹
  • ์ด๊ฒƒ์„ ๋งตํ•‘๋งŒ ํ•ด๋†“๊ณ  kva๋Š” ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ๊ธฐ์กด์˜ kva๋ฅผ ๋ฐ›์•„์˜ค๋Š” copy_claim_page๋ผ๋Š” ํ•จ์ˆ˜๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค๊ธฐ
void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
	/** Project 3-Memory Management */
	hash_init(spt, 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) {
	
	/** Project 3-Memory Mapped Files */
 	struct hash_iterator iter;
    hash_first(&iter, &src->spt_hash);
    while (hash_next(&iter)) {
        struct page *src_page = hash_entry(hash_cur(&iter), struct page, hash_elem);
        enum vm_type type = src_page->operations->type;
        void *upage = src_page->va;
        bool writable = src_page->writable;

        if (type == VM_UNINIT) {
            void *aux = src_page->uninit.aux;
            vm_alloc_page_with_initializer(page_get_type(src_page), upage, writable, src_page->uninit.init, aux);
        }

        else if (type == VM_FILE) {
            struct vm_load_arg *aux = malloc(sizeof(struct vm_load_arg));
            aux->file = src_page->file.file;
            aux->ofs = src_page->file.offset;
            aux->read_bytes = src_page->file.page_read_bytes;

            if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, aux))
                return false;

            struct page *dst_page = spt_find_page(dst, upage);
            file_backed_initializer(dst_page, type, NULL);
            dst_page->frame = src_page->frame;
            pml4_set_page(thread_current()->pml4, dst_page->va, src_page->frame->kva, src_page->writable);
        }

        else {                                          
            if (!vm_alloc_page(type, upage, writable)) 
                return false;

			/** Project 3-Copy On Write */
            // if (!vm_claim_page(upage))  
            //     return false;

            // struct page *dst_page = spt_find_page(dst, upage); 
            // memcpy(dst_page->frame->kva, src_page->frame->kva, PGSIZE);

			if (!vm_copy_claim_page(dst, upage, src_page->frame->kva, writable)) 
                return false;
        }
    }

    return true;
}

3. vm_copy_claim_page() ๊ตฌํ˜„

  • vm/vm.c
  • ํ•จ์ˆ˜์—์„œ frame์„ ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ๊นŒ์ง€๋Š” ๋™์ผํ•˜์ง€๋งŒ, ๊ธฐ์กด์˜ kva๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋งŒ๋“ค๊ณ  accessible์„ ์„ค์ •ํ•ด ์ค€ ํ›„ pml4๋ฅผ writable์„ false๋กœ swap_in์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  • ์ด์ œ fork๋œ ํŽ˜์ด์ง€์— ๋Œ€ํ•ด์„œ writable๊ฐ€ ๋ฌด์กฐ๊ฑด false์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ํŽ˜์ด์ง€์— write๋ฅผ ์‹œ๋„ํ•˜๋ฉด page_fault๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
/** Project 3-Copy On Write */
static bool 
vm_copy_claim_page(struct supplemental_page_table *dst, void *va, void *kva, bool writable) {
    struct page *page = spt_find_page(dst, va);

    if (page == NULL)
        return false;

    struct frame *frame = (struct frame *)malloc(sizeof(struct frame));

    if (!frame)
        return false;

    page->accessible = writable; 
    frame->page = page;
    page->frame = frame;
    frame->kva = kva;

    if (!pml4_set_page(thread_current()->pml4, page->va, frame->kva, false)) {
        free(frame);
        return false;
    }

    list_push_back(&frame_table, &frame->frame_elem); 

    return swap_in(page, frame->kva);
}

4. vm_try_handle_fault() ์ˆ˜์ •

  • vm/vm.c
  • ์ด์— ๋”ฐ๋ผ์„œ try_handle_fault ์—์„œ ์ด๋ฅผ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
  • ๊ทธ ๋™์•ˆ์€ writable์ด ๊ธˆ์ง€๋œ ํŽ˜์ด์ง€์— write๋ฅผ ์‹œ๋„ํ–ˆ์„ ๋•Œ ๋ฌด์กฐ๊ฑด false๋ฅผ ๋ฐ˜ํ™˜ํ–ˆ์—ˆ๋Š”๋ฐ ์ด๋ฅผ accessible์„ ๊ฒ€์‚ฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋„๋ก ์ˆ˜์ •ํ•œ๋‹ค.
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;
	/** Project 3-Copy On Write */
    struct page *page = spt_find_page(&thread_current()->spt, addr);

    if (addr == NULL || is_kernel_vaddr(addr))
        return false;

    if (!not_present && write)
        return vm_handle_wp(page);

    if (!page) {
        void *rsp = user ? f->rsp : thread_current()->stack_pointer;
		if (STACK_LIMIT <= rsp - 8 && rsp - 8 == addr && addr <= USER_STACK){
			vm_stack_growth(thread_current()->stack_bottom - PGSIZE);
			return true;
		}
		else if (STACK_LIMIT <= rsp && rsp <= addr && addr <= USER_STACK){
			vm_stack_growth(thread_current()->stack_bottom - PGSIZE);
			return true;
		}
        return false;
    }

    return vm_do_claim_page(page);  
}

5. vm_handle_wp() ๊ตฌํ˜„

  • vm/vm.c
  • ๋จผ์ € ํŽ˜์ด์ง€์— ๋Œ€ํ•ด์„œ ๊ฒ€์‚ฌํ•˜๋Š”๋ฐ access๊ฐ€ ๊ธˆ์ง€๋˜์–ด ์žˆ๋‹ค๋ฉด fork๋œ ํŽ˜์ด์ง€๊ฐ€ ์•„๋‹ˆ๋ผ ์‹ค์ œ๋กœ ์ ‘๊ทผํ•ด์„œ๋Š” ์•ˆ๋˜๋Š” ํŽ˜์ด์ง€์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋กœ false๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
  • ๋งŒ์•ฝ access ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด ์ด๋Š” fork๋œ ํŽ˜์ด์ง€๊ธฐ ๋–„๋ฌธ์— ๋จผ์ € ๊ธฐ์กด kva์„ ๋ฐฑ์—…ํ•ด์ฃผ๊ณ  kva์— user_pool์—์„œ ์ƒˆ๋กœ์šด ํŽ˜์ด์ง€๋ฅผ ํ• ๋‹นํ•œ๋‹ค.
    ์ด ๋•Œ ์šฉ๋Ÿ‰์ด ๊ฝ‰ ์ฐจ์„œ ํ• ๋‹นํ•  ์ˆ˜ ์—†๋‹ค๋ฉด evict_frame์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋งŒ๋“ค์–ด์ค€๋‹ค.
  • ๊ทธ ํ›„ ์›๋ณธ kva๋ฅผ ํ˜„์žฌ frame์— ๋ณต์›ํ•˜๊ณ  pml4์— ํ• ๋‹นํ•ด์ค€๋‹ค.
static bool
vm_handle_wp (struct page *page UNUSED) {
	/** Project 3-Copy On Write */
    if (!page->accessible)
        return false;

    void *kva = page->frame->kva;

    page->frame->kva = palloc_get_page(PAL_USER | PAL_ZERO);

    if (page->frame->kva == NULL)
        page->frame = vm_evict_frame();  
		
    memcpy(page->frame->kva, kva, PGSIZE);

    if (!pml4_set_page(thread_current()->pml4, page->va, page->frame->kva, page->accessible))
        return false;

    return true;
}

6. anon_destroy() ์ˆ˜์ •

  • vm/anon.c
  • ์ž์‹ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ•  ์ผ ๋ชจ๋‘ ๋๋‚ด๊ณ  ์ข…๋ฃŒํ•  ๋•Œ process_cleanup์„ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด ๋•Œ spt๋ฅผ ์ง€์šฐ๋Š” ๊ฒƒ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ pml4๋„ ๊ฐ™์ด ํŒŒ๊ดดํ•œ๋‹ค.
  • ์šฐ๋ฆฌ์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ถ€๋ชจ์˜ Anonymous_Page๋ฅผ ๊ฐ€์ ธ์™€์„œ write๋ฅผ ์‹œ๋„ํ•˜์ง€ ์•Š์€ ํŽ˜์ด์ง€๋“ค์€ ์•„์ง ๋ถ€๋ชจ์˜ kva๋ฅผ ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์ž์‹์ด exit ํ•  ๋•Œ ํ•ด๋‹น kva๊นŒ์ง€ ํŒŒ๊ดดํ•ด๋ฒ„๋ฆผ -> ๊ทธ๋ž˜์„œ ์ถ”ํ›„์— ๋ถ€๋ชจ๊ฐ€ ์ ‘๊ทผํ•˜๋ฉด fault๊ฐ€ ๋œจ๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ. ๐Ÿšจ
  • pml4_destroy๋ฅผ ๋ง‰๊ณ  pml4_clear๋งŒ ์ˆ˜ํ–‰ํ•˜์—ฌ kva์˜ ์—ฐ๊ฒฐ๋งŒ ๋Š์€ ์ƒํƒœ๋กœ ๋งŒ๋“ค์–ด ์žฌ๊ฐ€์šฉํ•˜๊ฒŒ ๋Œ๋ ค์ค€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜์ค‘์— OS๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๋งŒ kva๋ฅผ destory์‹œํ‚ค๋„๋ก ํ•œ๋‹ค.
static void
anon_destroy (struct page *page) {
	struct anon_page *anon_page = &page->anon;
	/** Project 3-Swap In/Out */
    if (anon_page->page_no != BITMAP_ERROR)
        bitmap_reset(swap_bitmap, anon_page->page_no);

    if (page->frame) {
		lock_acquire(&swap_lock);
        list_remove(&page->frame->frame_elem);
		lock_release(&swap_lock);
        page->frame->page = NULL;
        free(page->frame);
        page->frame = NULL;
    }
	pml4_clear_page(thread_current()->pml4, page->va); /** Project 3-Copy On Write */
}

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

pass tests/userprog/args-none
pass tests/userprog/args-single
pass tests/userprog/args-multiple
pass tests/userprog/args-many
pass tests/userprog/args-dbl-space
pass tests/userprog/halt
pass tests/userprog/exit
pass tests/userprog/create-normal
pass tests/userprog/create-empty
pass tests/userprog/create-null
pass tests/userprog/create-bad-ptr
pass tests/userprog/create-long
pass tests/userprog/create-exists
pass tests/userprog/create-bound
pass tests/userprog/open-normal
pass tests/userprog/open-missing
pass tests/userprog/open-boundary
pass tests/userprog/open-empty
pass tests/userprog/open-null
pass tests/userprog/open-bad-ptr
pass tests/userprog/open-twice
pass tests/userprog/close-normal
pass tests/userprog/close-twice
pass tests/userprog/close-bad-fd
pass tests/userprog/read-normal
pass tests/userprog/read-bad-ptr
pass tests/userprog/read-boundary
pass tests/userprog/read-zero
pass tests/userprog/read-stdout
pass tests/userprog/read-bad-fd
pass tests/userprog/write-normal
pass tests/userprog/write-bad-ptr
pass tests/userprog/write-boundary
pass tests/userprog/write-zero
pass tests/userprog/write-stdin
pass tests/userprog/write-bad-fd
pass tests/userprog/fork-once
pass tests/userprog/fork-multiple
pass tests/userprog/fork-recursive
pass tests/userprog/fork-read
pass tests/userprog/fork-close
pass tests/userprog/fork-boundary
pass tests/userprog/exec-once
pass tests/userprog/exec-arg
pass tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
pass tests/userprog/exec-read
pass tests/userprog/wait-simple
pass tests/userprog/wait-twice
pass tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
pass tests/userprog/multi-recurse
pass tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
pass tests/userprog/rox-child
pass tests/userprog/rox-multichild
pass tests/userprog/bad-read
pass tests/userprog/bad-write
pass tests/userprog/bad-read2
pass tests/userprog/bad-write2
pass tests/userprog/bad-jump
pass tests/userprog/bad-jump2
pass tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
pass tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
pass tests/vm/pt-write-code
pass tests/vm/pt-write-code2
pass tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
pass tests/vm/page-merge-par
pass tests/vm/page-merge-stk
pass tests/vm/page-merge-mm
pass tests/vm/page-shuffle
pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
pass tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
pass tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
pass tests/vm/swap-file
pass tests/vm/swap-anon
pass tests/vm/swap-iter
pass tests/vm/swap-fork
pass tests/filesys/base/lg-create
pass tests/filesys/base/lg-full
pass tests/filesys/base/lg-random
pass tests/filesys/base/lg-seq-block
pass tests/filesys/base/lg-seq-random
pass tests/filesys/base/sm-create
pass tests/filesys/base/sm-full
pass tests/filesys/base/sm-random
pass tests/filesys/base/sm-seq-block
pass tests/filesys/base/sm-seq-random
pass tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
pass tests/filesys/base/syn-write
pass tests/threads/alarm-single
pass tests/threads/alarm-multiple
pass tests/threads/alarm-simultaneous
pass tests/threads/alarm-priority
pass tests/threads/alarm-zero
pass tests/threads/alarm-negative
pass tests/threads/priority-change
pass tests/threads/priority-donate-one
pass tests/threads/priority-donate-multiple
pass tests/threads/priority-donate-multiple2
pass tests/threads/priority-donate-nest
pass tests/threads/priority-donate-sema
pass tests/threads/priority-donate-lower
pass tests/threads/priority-fifo
pass tests/threads/priority-preempt
pass tests/threads/priority-sema
pass tests/threads/priority-condvar
pass tests/threads/priority-donate-chain
pass tests/vm/cow/cow-simple
All 141 tests passed.

ec2 ํ™˜๊ฒฝ์—์„œ swap-fork๊ฐ€ FAILํ•˜์˜€์ง€๋งŒ, WSL ํ™˜๊ฒฝ์—์„œ passํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•จ.

ํ•€ํ† ์Šค ๋!!!!!!!! ๐Ÿ˜‡

profile
Sunny Day!

0๊ฐœ์˜ ๋Œ“๊ธ€