supplemental_page_table_copy() (vm/vm.c)src와 dst를 선언하고,
해쉬의 struct를 가져온다(hash_elem)
가져온 해쉬 테이블을 순회하고 → 시작 지점을 구하고, hash struct size, hash_elem 주소를 받는다.
페이지 복제()
복제할 때 뭐가 필요하지?
src→커널 주소?
aux? → 얘는 어떤 정보를 가지고 있어야 하는거지?
file backed을 복제해줘야 한다?
claim으로 spt->page 와 실제 메모리 공간을 매핑한다.
현재 가상 메모리의 커널 주소와 실제 메모리 공간을 매칭시켜야 하는데…
실제 물리 메모리 주소는 어떻게 아는 거지??
spt와 연결된 frame 공간에 메모리 복제()를 해야한다?
메모리 복제 시에 복제할 메모리와 복제할 위치를 받아야 한다.
bool supplemental_page_table_copy(struct supplemental_page_table *dst,
struct supplemental_page_table *src)
{
struct hash_iterator i;
hash_first(&i, &src->hash);
while (hash_next(&i))
{
struct page *src_page = hash_entry(hash_cur(&i), struct page, hash_elem);
enum vm_type type = page_get_type(src_page);
void *upage = src_page->va;
// 1. 새로운 페이지를 dst SPT에 할당
if (!vm_alloc_page_with_initializer(type, upage, src_page->writable,
src_page->uninit.init, src_page->uninit.aux))
{
return false;
}
// 2. 새로 할당된 페이지를 찾고 claim
struct page *dst_page = spt_find_page(dst, upage);
if (!vm_claim_page(upage))
{
return false;
}
// 3. 부모의 프레임이 존재하면, 자식의 프레임으로 데이터 복사
if (src_page->frame != NULL)
{
memcpy(dst_page->frame->kva, src_page->frame->kva, PGSIZE);
}
}
return true;
}
void *mmap(...) 시스템 콜if → addr == 0, length == 0, spt_find() ≠ NULL, fd == 0, fd == 1, addr % 4096 ≠ 0이라면 NULL을 반환한다.
file_reopen()
page = do_mmap()
return addr
// 1. addr이 0인지 check한다
// 2. fd가 0이나 1이 아닌지 check한다
// 3.file 용량이 0인지 check 한다.
// 4. addr의 page에 spt가 없어야 한다?
// 5. addr aline인있는 지 check
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset)
{
struct thread *curr = thread_current();
if (addr == NULL || length == 0 || fd == 0 || fd == 1 || (uint64_t) addr % 4096 != 0 || spt_find_page(&curr->spt, addr)) {
return NULL;
}
sema_down(&filesys_lock);
struct file *file = open(curr->fd_table[fd]);
sema_up(&filesys_lock);
void *upage = do_mmap(addr, length, writable, file, offset);
sema_down(&filesys_lock);
close(fd);
sema_up(&filesys_lock);
return upage;
}
do_munmap()/* Do the mmap */
void *
do_mmap(void *addr, size_t length, int writable, struct file *file, off_t offset)
{
// bool vm_alloc_page_with_initializer(enum vm_type type, void *upage, bool writable, vm_initializer *init, void *aux)
struct lazy_aux_file_backed *aux;
file_seek(file, offset);
while(length > 0)
{
aux = malloc(sizeof(struct lazy_aux_file_backed));
aux->file = file; // 필요한가??
aux->writable = writable;
if(length > PGSIZE) aux->length = PGSIZE;
else aux->length = length;
aux->offset = offset;
vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_file_backed, aux); // TODO: 예외처리 필요
// 쓴 만큼 offset, length 업데이트.
offset += PGSIZE;
length -= PGSIZE;
addr = (void *)((char *) addr + PGSIZE);
}
// 1. addr로부터 페이지 생성
// 1-1. lazy_load, aux 초기화해서 넘겨주기.
// 1-2. 복사(length, offset, 등등) 이거 바로 해줘요? 그럼 또 lazy 아니잖아. -> 이 내용이 lazy_load에서 타입 체크후에 복사 바로 하면 되지 않겠나.
// 1-3. 나머자 내용은 0으로 채워야 함.
}
static bool
lazy_load_file_backed(struct page *page, void *aux)
{
/* 파일에서 페이지 컨텐츠를 읽어옵니다. */
/* 이 함수는 주소 VA에서 첫 페이지 폴트가 발생했을 때 호출됩니다. */
/* 이 함수를 호출할 때 VA를 사용할 수 있습니다. */
dprintfd("[lazy_load_file_backed] routine start. page: %p, page->va: %p\n", page, page->va);
void *va = page->va;
memset(page->frame->kva, 0, PGSIZE); // zero bytes 복사.
/* Load this page. */
struct lazy_aux_file_backed *lazy_aux = (struct lazy_aux_file_backed *)aux;
// aux 멤버 정의 필요.
dprintfd("[lazy_load_file_backed] reading file\n");
if (file_read_at(lazy_aux->file, page->frame->kva, lazy_aux->length, lazy_aux->offset) != (int)lazy_aux->length)
{
free(lazy_aux);
return false;
}
return true;
}
vm_try_handle_fault() 수정 (vm/vm.c)// - 페이지 폴트가 발생하면 `userprog/exception.c`의 `page_fault()`에서 `vm_try_handle_fault` 함수를 호출합니다.
// - 이 함수에서 **해당 페이지 폴트가 스택 확장으로 처리 가능한지**를 판단해야 합니다.
// - 판단 기준을 만족하면 `vm_stack_growth()`를 호출해 해당 주소까지 스택을 확장합니다.
bool vm_try_handle_fault(struct intr_frame *f, void *addr,
bool user, bool write, bool not_present)
{
dprintfc("[vm_try_handle_fault] fault handle start. addr: %p\n", addr);
struct supplemental_page_table *spt = &thread_current()->spt; // 현재 쓰레드의 spt 가져옴.
struct page *page;
if ((f->rsp - 8 == addr)) // stack growth 경우
{
dprintfc("[vm_try_handle_fault] expending stack page\n");
thread_current()->rsp = f->rsp;
vm_stack_growth(addr);
}
page = spt_find_page(spt, addr); // page를 null로 설정해. stack growth 경우에는 spt 찾을 필요 없지 않나? 어차피 없을텐데.
return vm_do_claim_page(page); // 그 페이지에 대응하는 프레임을 할당받아.
}
vm_stack_growth() 구현 (vm/vm.c)/* 스택 확장 */
// - 주어진 주소 `addr`까지 스택 크기를 확장합니다.
// - 하나 이상의 **익명 페이지(anonymous page)**를 할당해 폴트가 더 이상 발생하지 않도록 합니다. // HACK: "하나 이상?"
// - 이때 **`addr`은 반드시 페이지 단위(PGSIZE)로 내림(round down)** 처리 후 할당해야 합니다.
// - 대부분의 운영체제는 **스택의 최대 크기를 제한**합니다.
// - 예: 유닉스 계열의 `ulimit` 명령으로 조정 가능
// - GNU/Linux 시스템의 기본값: 약 **8MB**
// - 본 프로젝트에서는 스택 최대 크기를 **1MB**로 제한해야 합니다. // HACK: 제한 안 걸어 놨음.
static void
vm_stack_growth(void *addr)
{
// HACK: thread_current()->rsp 는 건들지도 않았는데 구현 됨. 이거 왜 하라는 거임?
dprintfc("[vm_stack_growth] routine start\n");
bool result = vm_alloc_page_with_initializer(VM_ANON | VM_MARKER_STACK, addr, true, NULL, NULL);
dprintfc("[vm_stack_growth] vm_alloc complete. result: %d\n", result);
}
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
FAIL tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
FAIL tests/vm/pt-write-code
FAIL tests/vm/pt-write-code2
FAIL tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
FAIL tests/vm/page-merge-par
FAIL tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
FAIL tests/vm/mmap-read
FAIL tests/vm/mmap-close
FAIL tests/vm/mmap-unmap
FAIL tests/vm/mmap-overlap
FAIL tests/vm/mmap-twice
FAIL tests/vm/mmap-write
FAIL tests/vm/mmap-ro
FAIL tests/vm/mmap-exit
FAIL tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
FAIL tests/vm/mmap-clean
FAIL 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
FAIL 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
FAIL tests/vm/mmap-off
FAIL tests/vm/mmap-bad-off
FAIL tests/vm/mmap-kernel
FAIL tests/vm/lazy-file
pass tests/vm/lazy-anon
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL 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
FAIL tests/vm/cow/cow-simple
27 of 141 tests failed.