메모리 스와핑은 물리적 메모리 사용을 최대화하기 위한 메모리 회수 기술입니다. 메인 메모리의 프레임이 할당되면 시스템은 사용자 프로그램의 메모리 할당 요청을 더 이상 처리할 수 없습니다. 한 가지 솔루션은 현재 디스크에 사용되지 않는 메모리 프레임을 교체하는 것입니다. 이렇게 하면 일부 메모리 리소스를 확보하여 다른 응용 프로그램에서 사용할 수 있습니다.
스와핑은 운영 체제에서 수행됩니다. 시스템이 메모리 부족을 감지했지만 메모리 할당 요청을 받으면, 스왑 디스크를 제거할 페이지를 선택합니다.(When the system detects that it has run out of memory but receives a memory allocation request, it chooses a page to evict out to swap disk.)
그러면 메모리 프레임의 정확한 상태가 디스크에 복사됩니다. 프로세스가 스왑 아웃된 페이지에 액세스하려고 하면 OS는 정확한 내용을 메모리로 다시 가져와 페이지를 복구합니다.
제거를 위해 선택한 페이지는 익명 페이지 또는 파일 백업 페이지(file-backed page)일 수 있습니다. 이 섹션에서는 각 케이스를 처리합니다.
모든 스와핑 작업은 명시적으로 호출되지 않고, 함수 포인터로 호출됩니다. 이들은 struct page_operations file_ops
의 멤버로, 각 페이지의 이니셜라이저에 대한 작업으로 등록됩니다.
익명 페이지
vm/anon.c
의 vm_anon_init
와 anon_initializer
를 수정하세요.
익명 페이지에는 백업 저장소가 없습니다. 익명 페이지의 스와핑을 지원하기 위해 스왑 디스크라는 임시 백업 저장소를 제공합니다. 스왑 디스크를 활용하여 익명 페이지에 대한 스왑을 구현합니다.
void vm_anon_init (void);
이 기능에서는 스왑 디스크를 설정해야 합니다. 또한 스왑 디스크에서 사용 가능한 영역과 사용된 영역을 관리하기 위한 데이터 구조가 필요합니다. 스왑 영역도 PGSIZE(4096바이트) 단위로 관리됩니다.
/* Initialize the data for anonymous pages */
void
vm_anon_init (void) {
/* TODO: Set up the swap_disk. */
swap_disk = disk_get(1, 1);
bitcnt = disk_size(swap_disk)/SECTORS_IN_PAGE; // #ifdef Q. disk size decided by swap-size option?
swap_table = bitmap_create(bitcnt); // each bit = swap slot for a frame
}
bool anon_initializer (struct page *page, enum vm_type type, void *kva);
이것은 익명 페이지의 이니셜라이저입니다. anon_page스와핑을 지원하려면 에 몇 가지 정보를 추가해야 합니다 .
이제 익명 페이지에 대한 스와핑을 지원하기 위해 anon_swap_in
및 anon_swap_out
을 구현합니다.
vm/anon.c
스왑 인하려면 페이지를 스왑 아웃해야하므로 구현 anon_swap_out
하기 전에 구현하는 것이 anon_swap_in
좋습니다. 데이터 내용을 스왑 디스크로 이동하고 안전하게 메모리로 가져와야 합니다.
/* Initialize the file mapping */
bool
anon_initializer (struct page *page, enum vm_type type, void *kva) {
struct uninit_page *uninit = &page->uninit;
memset(uninit, 0, sizeof(struct uninit_page)); // use after free 취약점(이전 데이터를 타인이 읽는 것) 방지 (zeroing) / 사실 page의 kva를 멤셋해야..
/* Set up the handler */
page->operations = &anon_ops;
struct anon_page *anon_page = &page->anon;
anon_page->swap_sec = -1;
return true;
}
static bool anon_swap_in (struct page *page, void *kva);
디스크에서 메모리로 데이터 내용을 읽어 스왑 디스크에서 익명 페이지로 스왑합니다. 데이터의 위치는 페이지가 스왑 아웃될 때 페이지 구조에 스왑 디스크가 저장되어 있어야 합니다. 스왑 테이블을 업데이트해야 합니다( 스왑 테이블 관리 참조 ).
/* Swap in the page by read contents from the swap disk. */
static bool
anon_swap_in (struct page *page, void *kva) {
struct anon_page *anon_page = &page->anon;
int swap_sec = anon_page->swap_sec;
int swap_slot_idx = swap_sec / SECTORS_IN_PAGE;
if(bitmap_test(swap_table, swap_slot_idx) == 0)
//return false;
PANIC("(anon swap in) Frame not stored in the swap slot!\n");
page->frame->kva = kva;
bitmap_set(swap_table, swap_slot_idx, 0);
// ASSERT(is_writable(kva) != false);
// disk_read is done per sector; repeat until one page is read
for(int sec = 0; sec < SECTORS_IN_PAGE; sec++)
disk_read(swap_disk, swap_sec + sec, page->frame->kva + DISK_SECTOR_SIZE * sec);
// restore vaddr connection
pml4_set_page(thread_current()->pml4, page->va, kva, true); // writable true, as we are writing into the frame
anon_page->swap_sec = -1;
return true;
}
static bool anon_swap_out (struct page *page);
메모리에서 디스크로 내용을 복사하여 익명 페이지를 스왑 디스크로 교체합니다. 먼저 스왑 테이블을 사용하여 디스크에서 여유 스왑 슬롯을 찾은 다음 데이터 페이지를 슬롯에 복사합니다. 데이터의 위치는 페이지 구조에 저장해야 합니다. 디스크에 여유 슬롯이 더 이상 없으면 커널을 패닉 상태로 만들 수 있습니다.
/* Swap out the page by writing contents to the swap disk. */
static bool
anon_swap_out (struct page *page) {
struct anon_page *anon_page = &page->anon;
// Find free slot in swap disk
// Need at least PGSIZE to store frame into the slot
// size_t free_idx = bitmap_scan(swap_table, 0, SECTORS_IN_PAGE, 0);
size_t free_idx = bitmap_scan_and_flip(swap_table, 0, 1, 0);
if(free_idx == BITMAP_ERROR)
PANIC("(anon swap-out) No more free swap slots!\n");
int swap_sec = free_idx * SECTORS_IN_PAGE;
// disk_write is done per sector; repeat until one page is written
for(int sec = 0; sec < SECTORS_IN_PAGE; sec++)
disk_write(swap_disk, swap_sec + sec, page->frame->kva + DISK_SECTOR_SIZE * sec);
// access to page now generates fault
pml4_clear_page(thread_current()->pml4, page->va);
anon_page->swap_sec = swap_sec;
page->frame->page = NULL;
page->frame = NULL;
return true;
}
파일 매핑 페이지
파일 백업 페이지의 내용은 파일에서 가져오기 때문에 mmaped 파일을 백업 저장소로 사용해야 합니다. 즉, 파일 백업 페이지를 제거하면 해당 페이지가 매핑된 파일에 다시 기록됩니다.
vm/file.c
에서 file_backed_swap_in,
file_backed_swap_out
를 구현합니다. 디자인(design)에 따라 file_backed_init
와 file_initializer
를 수정할 수 있습니다.
static bool file_backed_swap_in (struct page *page, void *kva);
파일에서 내용을 읽어
kva
에서 페이지를 교체(swaps)합니다.
파일 시스템과 동기화해야 합니다.
/* Swap in the page by read contents from the file. */
static bool
file_backed_swap_in (struct page *page, void *kva) {
struct file_page *file_page UNUSED = &page->file;
ASSERT(page->frame->kva == kva); // #ifdef DBG check
void *addr = page->va;
//struct thread *t = thread_current();
//aren't these already set in vm_do_claim_page?
//page->frame->kva = kva;
//pml4_set_page(t->pml4, addr, kva, true); // writable true, as we are writing into the frame
struct file *file = file_page->file;
size_t length = file_page->length;
off_t offset = file_page->offset;
if(file_read_at(file, kva, length, offset) != length){
// TODO - Not properly written-back
}
return true;
}
static bool file_backed_swap_out (struct page *page);
파일에 내용을 다시 기록하여 페이지를 교체(swaps)합니다. 페이지가 dirty인지 먼저 확인하는 것이 좋습니다. dirty가 아니라면 파일의 내용을 수정할 필요가 없습니다. 페이지를 스왑한 후, 페이지의 더티 비트를 끄는 것을 잊지 마십시오.
/* Swap out the page by writeback contents to the file. */
static bool
file_backed_swap_out (struct page *page) {
struct file_page *file_page UNUSED = &page->file;
void *addr = page->va;
struct thread *t = thread_current();
if(pml4_is_dirty(t->pml4, addr)){
struct file *file = file_page->file;
size_t length = file_page->length;
off_t offset = file_page->offset;
void *kva = page->frame->kva;
if(file_write_at(file, kva, length, offset) != length){
// TODO - Not properly written-back
}
}
// access to page now generates fault
pml4_clear_page(t->pml4, addr);
page->frame->page = NULL;
page->frame = NULL;
return true;
}
vm_init
에 list_init 추가 + try_handler
주석 해제 + vm.c
→ frame_table
맨 위 define 주석 처리hash_action_destroy
→ remove 주석 처리중간중간 수정한 사항이 있는데 블로그에 쓰기를 놓친 코드들도 있을테니 깃에서 확인 하는 것을 추천합니다...
https://github.com/Yerimi11/pintos-kaist-team09
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
FAIL tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
FAIL 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
FAIL 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
FAIL 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
FAIL tests/vm/cow/cow-simple
8 of 141 tests failed.