void munmap(void *addr);
매핑된 addr을 받아서 매핑을 해제한다.
mmap()한 걸 해제할 때
→ munmap(void *addr)
중요: vm_alloc_page_with_initializer에서 말하는 initializer는 anon_initializer, file_backed_initializer같은 페이지 이니셜라이저고, lazy_load_segment 같은 vm_initializer와는 다르다.Page initialize 과정은 “지정“과 “실행“으로 나눠 볼 수 있다.
do_munmap()
전달받은 addr을 참조하여 실제 메모리 매핑 해제 작업을 수행한다.
→ vlidation()
→ file backed destroy()
-> ...
void
test_main (void)
{
int handle;
void *map;
CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
CHECK ((map = mmap (ACTUAL, 0x2000, 0, handle, 0)) != MAP_FAILED, "mmap \"sample.txt\"");
msg ("memory is readable %d", *(int *) ACTUAL);
msg ("memory is readable %d", *(int *) ACTUAL + 0x1000);
munmap (map);
fail ("unmapped memory is readable (%d)", *(int *) (ACTUAL + 0x1000));
fail ("unmapped memory is readable (%d)", *(int *) (ACTUAL));
}
설명 : 이 테스트는 mmap()에 의해 매핑된 메모리가 실제로 읽을 수 있는지와, munmap() 이후에 해당 메모리에 접근하면 실패(즉, 페이지 폴트)가 발생해야 하는지를 검증하는 테스트.
(mmap-unmap) begin
(mmap-unmap) open "sample.txt"
(mmap-unmap) mmap "sample.txt"
Kernel PANIC at ../../lib/kernel/list.c:158 in list_insert(): assertion `is_interior (before) || is_tail (before)' failed.
0x000000800421802e: debug_panic (lib/kernel/debug.c:32)
0x0000008004218577: list_insert (lib/kernel/list.c:159)
0x000000800421881f: list_push_back (lib/kernel/list.c:204)
0x0000008004221b43: do_mmap (vm/file.c:127)
0x000000800421d55d: mmap (userprog/syscall.c:286)
0x000000800421d8c5: syscall_handler (userprog/syscall.c:376)
0x000000800421ce7c: no_sti (userprog/syscall-entry.o:?)
mmap_file: 0x8004248138
mmap_file->elem.prev: 0xcccccccccccccccc
mmap_file->elem.next: 0xcccccccccccccccc
before가 잘못된 주소를 참조하고 있었음.
#ifdef VM
list_init(&t->mmap_list); // feat: do_munmap
#endif
해결
mmap_file: 0x8004248138
mmap_file->elem.prev: 0xcccccccccccccccc
mmap_file->elem.next: 0xcccccccccccccccc
[do_mmap] list_push_back() 이후
mmap-unmap: exit(-1)
mmap-unmap: exit(-1)
mmap()으로 매핑된 file을 읽지 못하고 있음.
000000800422199b: file_backed_destroy (vm/file.c:83)
0x0000008004221178: vm_dealloc_page (vm/vm.c:367)
0x00000080042215fb: spt_destructor (vm/vm.c:505)
0x000000800421a613: hash_clear (lib/kernel/hash.c:59)
0x00000080042215cd: supplemental_page_table_kill (vm/vm.c:499)
0x000000800421c107: process_cleanup (userprog/process.c:458)
0x000000800421c0a3: process_exit (userprog/process.c:441)
0x0000008004207240: thread_exit (threads/thread.c:328)
0x000000800421d009: write (userprog/syscall.c:98)
0x000000800421ce1e: page_fault (userprog/exception.c:152)
문제 정의
mmap-unmap: exit(-1)
mmap-unmap: exit(-1)
위에서 file backed destroy()가 중복되는 문제 해결
if(spt_find_page(spt, page->va) != NULL && page->operations != NULL && page->frame != NULL){
if (pml4_is_dirty(pml4, page->va))
{
file_write_at(file_page->file, page->va, file_page->size, file_page->file_ofs); // Writes SIZE bytes만큼 쓴다.
}
file_close(file_page->file);
dprintfg("[file_backed_destroy] spt remove page()할 때 문제가 발생한 것 같아\n");
// spt_remove_page(spt, page); // spt 제거 -> spt에서 지우면 pml4에서 계속 업데이트가 된다?
}
}
(mmap-unmap) begin
(mmap-unmap) open "sample.txt"
(mmap-unmap) mmap "sample.txt"
[do_mmap] length: 4096, offset: 4096, addr: 0x10001000
[do_mmap] length: 0, offset: 8192, addr: 0x10002000
[do_mmap] list_push_back() 이전
mmap_file: 0x8004248138
mmap_file->elem.prev: 0xcccccccccccccccc
mmap_file->elem.next: 0xcccccccccccccccc
mmap_file->elem.prev: 0x8004243070
mmap_file->elem.next: 0x8004243080
[do_mmap] list_push_back() 이후
[lazy_load_file_backed] routine start. page: 0x8004246498, page->va: 0x10000000
[lazy_load_file_backed] reading file
mmap-unmap: exit(-1)
[file backed destroy] dirty bit를 확인하기 전
Execution of 'mmap-unmap' complete.
한참 찾았는데 file backed destroy()에서 spt_remove_page()을 호출하게 되면 spt_remove_page()에서 다시 destroy()를 호출하게 되면서 이미 해제된 메모리를 다시 접근하는 문제가 발생한다.
static void
file_backed_destroy(struct page *page)
{
// - 파일 기반 페이지를 제거하는 함수
// - 페이지가 **dirty 상태**면, 변경 사항을 파일에 기록(write-back)해야 함
// - 여기서 `page` 구조체 자체를 `free`할 필요는 없음 → 호출자가 해제함
// - 호출자 = spt_remove_page → vm_dealloc_page
// - 여기서 destroy 호출 후 구조체 free까지 해줌
// - destroy에서 구현할 로직?
// - 매핑된 프레임 해제?
// - spt_remove_page에서 구현하는것이 좋을듯하다
// - write-back 구현
struct file_page *file_page = &page->file;
struct pml4 *pml4 = thread_current()->pml4;
struct supplemental_page_table *spt = &thread_current()->spt;
dprintfg("[file backed destroy] dirty bit를 확인하기 전\n");
if(spt_find_page(spt, page->va) != NULL && page->operations != NULL && page->frame != NULL){
if (pml4_is_dirty(pml4, page->va))
{
file_write_at(file_page->file, page->va, file_page->size, file_page->file_ofs); // Writes SIZE bytes만큼 쓴다.
}
file_close(file_page->file);
dprintfg("[file_backed_destroy] spt remove page()할 때 문제가 발생한 것 같아\n");
// spt_remove_page(spt, page); // spt 제거 -> spt에서 지우면 pml4에서 계속 업데이트가 된다?
}
}
spt_remove_page()은 do_munmap()에서 호출해야 한다.