이번엔 Unmapping 관련 내용을 다뤄볼건데, 기본 내용은 이전 게시글과 같다.
그러니 munmap syscall & do_munmap에 관해서만 다루겠다.
그렇다 해도 unmapping 관련해서 수정/구현해야 할 내용이 훨씬 많다ㅎ...
먼저 git book을 기반으로 한 내용들을 정리해보면 다음과 같다.
매핑을 해제할 때 고려해야 할 점이 있다.
만약 매핑이 진행된 후 수정사항이 있었다면 기존 파일에 수정사항을 반영해야 한다.
이 사실을 기억하고 있는 상태로 해당 파트를 진행해보겠다.
본격적으로 munmap syscall을 구현하려면 예전에 anon_page 관련 함수를 구현할 때도 슬쩍슬쩍 보였던 file_backed_page 관련 함수들을 먼저 구현해야 한다.
위에서 작성한 것처럼 매핑을 해제할 때, 파일에 대한 수정사항이 있다면 기존 파일, 즉 disk의 파일에 수정사항을 기록해주어야 한다.
이를 위해선 파일에 대한 정보를 페이지가 갖고 있어야겠지?
그 후 수정사항을 기록한 이후 프로세스의 가상 페이지 목록에서 페이지를 제거함으로써 매핑을 해제하도록 한다.
근데 이 때, 페이지 구조체에 파일 정보를 담기 위해 file_page 구조체에 관련 멤버 변수 추가해주는 것을 잊지 말자!
- include/vm/file.h
struct file_page {
struct file *file;
off_t ofs;
uint32_t read_bytes;
uint32_t zero_bytes;
};
수정사항을 파일에 기록하기 위해서는 매핑을 해제할 때 해당 페이지에 매핑된 파일에 대한 정보를 알아야 한다.
그러니 file_backed_page가 초기화될 때 파일에 대한 정보를 page 구조체에 추가해준다.
- vm/file.c
bool
file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
/* Set up the handler : page에 file_backed_page에 대한 핸들러 설정 */
page->operations = &file_ops;
struct file_page *file_page = &page->file;
/* 매핑을 해제할 때, 파일에 대한 수정사항을 기존 파일에 기록하기 위해 */
/* 파일에 대한 정보를 페이지에 저장해두어야 함*/
/* file_page에 파일에 대한 정보 추가 */
struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)page->uninit.aux;
file_page->file = lazy_load_arg->file;
file_page->ofs = lazy_load_arg->ofs;
file_page->read_bytes = lazy_load_arg->read_bytes;
file_page->zero_bytes = lazy_load_arg->zero_bytes;
}
프로세스가 종료될 때 매핑이 해제되어야 하므로 수정사항을 파일에 반영하고, 가상 페이지 목록에서 제거하기
- vm/file.c
static void
file_backed_destroy (struct page *page) {
struct file_page *file_page UNUSED = &page->file;
/* 수정사항이 있었다면 file_write_at으로 반영하고 dirty를 0으로 수정 */
if (pml4_is_dirty(thread_current()->pml4, page->va))
{
file_write_at(file_page->file, page->va, file_page->read_bytes, file_page->ofs);
pml4_set_dirty(thread_current()->pml4, page->va, 0);
}
/* 가상페이지 목록에서 제거 */
pml4_clear_page(thread_current()->pml4, page->va);
}
void syscall_handler(struct intr_frame *f)
{
...
case SYS_MUNMAP:
do_munmap(f->R.rdi);
break;
...
}
같은 파일이 매핑된 여러 페이지들이 모두 해제될 수 있도록 매핑할 때 저장해둔 '매핑에 사용된 총 페이지 수'를 활용해 전부 해제하도록 한다.
page를 destroy함으로써 file_backed_destroy를 통해 파일의 수정사항을 기록하고 가상 페이지 목록에서 제거되도록 구현해야 한다.
void
do_munmap (void *addr) {
struct supplemental_page_table *spt = &thread_current()->spt;
struct page *p = spt_find_page(spt, addr);
/* 같은 파일이 매핑된 페이지가 모두 해제되도록 총 매핑된 페이지 수를 가져와 전부 해제 */
/* destroy 호출로 file_backed_destroy에서 파일의 수정사항을 기록하고 가상 페이지 목록에서 해당 페이지가 제거되도록 함 */
int mapped_pages = p->mapped_page_cnt;
for (int i = 0; i < mapped_pages; i++)
{
if (p)
{
destroy(p);
}
addr += PGSIZE;
p = spt_find_page(spt, addr);
}
}
여기서 추가로 supplemental_page_table_copy()를 수정해줘야 한다.
page type이 VM_FILE(=file_backed_page)일 경우, 자식 페이지가 부모 프레임과 매핑될 수 있도록 분기점을 추가하도록 한다.
/* 자식 프로세스 생성할 때 spt 복사하는 함수 */
/* src를 dst에 복사 */
bool supplemental_page_table_copy(struct supplemental_page_table *dst UNUSED, struct supplemental_page_table *src UNUSED)
{
...
/* page가 file_backed type이라면 */
if (type == VM_FILE)
{
struct lazy_load_arg *file_aux = malloc(sizeof(struct lazy_load_arg));
file_aux->file = src_page->file.file;
file_aux->ofs = src_page->file.ofs;
file_aux->read_bytes = src_page->file.read_bytes;
file_aux->zero_bytes = src_page->file.zero_bytes;
if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, file_aux))
{
return false;
}
struct page *file_page = spt_find_page(dst, upage);
file_backed_initializer(file_page, type, NULL);
file_page->frame = src_page->frame;
pml4_set_page(thread_current()->pml4, file_page->va, src_page->frame->kva, src_page->writable);
continue;
}
/* page가 anon type이라면 */
...
}
이렇게 했을 때 나는 11/141 passed가 떴다.
pt-write-code2 tc가 통과되지 않았었는데, sys_read를 수정해서 10/141 passed로 이번 파트를 마무리 지었다.
이는 pt-write-code2를 pass하기 위해 수정한 목록이다.
int read(int fd, void *buffer, unsigned size)
{
/* read 시스템 콜은 파일에서 데이터를 읽어와 메모리 버퍼에 저장하는 함수인데 */
/* spt_find_page를 통해서 buffer 주소에 대한 page를 얻어왔을 때 */
/* 해당 페이지가 not writable한다면, 즉 buffer에 읽어온 데이터를 쓸 수 없다면, exit */
struct page *page = spt_find_page(&thread_current()->spt, buffer);
if (page){
if (!page->writable) {
exit(-1);
}
}
if (!is_user_vaddr(buffer) || buffer == NULL){
exit(-1);
}
...
}
void check_address(void *addr)
{
struct thread *current = thread_current();
if (addr == NULL || is_kernel_vaddr(addr) || pml4_get_page(current->pml4, addr) == NULL)
exit(-1);
}
void vm_init(void)
{
...
// list_init(&frame_table);
// lock_init(&frame_table_lock);
}
vm_get_frame(void)
{
...
// lock_acquire(&frame_table_lock);
// list_push_back(&frame_table, &frame->frame_elem);
// lock_release(&frame_table_lock);
...
}
해당 부분까지 수정하게 되면 10/141 passed가 뜬다.
이렇게 하면 memory mapped files 파트도 끝이 난다.
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
FAIL tests/vm/page-merge-par
FAIL tests/vm/page-merge-stk
FAIL 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
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
FAIL 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
FAIL tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
FAIL 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
10 of 141 tests failed.