04) Memory Mapped Files
์์คํ
์ฝ mmap
๊ณผ munmap
์ ๊ตฌํํ๋ค. ํ์ฌ๊น์ง๋ ๋ชจ๋ ํ์ด์ง๊ฐ anonymous, ์ฆ ์คํ ํ์ด์ง์์ง๋ง mmap
์ ํตํด ํ์ผ๊ณผ ๋งคํ๋ ํ์ด์ง๋ file-backed page
๊ฐ ๋๋ค. ๋ฐ๋ผ์ ๋งคํ์ด ํด์ ๋ ๋ ๋ณ๊ฒฝ์ฌํญ์ ํ์ผ์ ๋ค์ ๊ธฐ๋กํด์ฃผ์ด์ผ ํ๋ค. ๋์คํฌ์ ํ์ผ์ ์ฐ๋ ์์
์ RAM๋๋น ์๋๊ฐ ๋งค์ฐ ๋๋ฆฌ๊ธฐ ๋๋ฌธ์, ๋ณ๊ฒฝ์ฌํญ์ด ์๋ ํ์ด์ง๋ง ๋์คํฌ์ ๊ธฐ๋กํด์ค ์ ์๋๋ก ํ๋ค. ํ์ด์ง๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ, mmu๋ ์์์ pml4 ํ
์ด๋ธ์ dirty bit๋ฅผ ์ธํ
ํด์ค๋ค.
/* Write back a single page to file. Called when unmap. */
static bool file_write_back(struct page *page, struct page *head) {
ASSERT(page->operations->type == VM_FILE)
struct thread *curr = thread_current();
if (!pml4_is_dirty(curr->pml4, page->va)) {
/* If page is present and
* not dirty, return. */
return true;
}
/* Write back to file. */
if (!do_file_io(page, head, filesys_write)) {
printf("Fail to write back file.\n");
return false;
}
return true;
}
// filesys.c
off_t filesys_write(struct file *file, const void *buf, off_t size) {
int bytes;
lock_acquire(&filesys_lock);
bytes = file_write(file, buf, size);
lock_release(&filesys_lock);
return bytes;
}
์ ํจ์๋ ํ๋์ ํ์ด์ง๊ฐ ๋ณ๊ฒฝ์ฌํญ์ด ์๋์ง๋ฅผ ํ์ธํ๊ณ , ์์ ๊ฒฝ์ฐ ํ์ผ์ ๋ค์ ์ฐ๋ ์์
์ ์ํํ๋ค. ํํ ์ค์ ํ๋ก์ ํธ 3๊น์ง๋ ํ์ผ์์คํ
์ ์ ๊ทผํ ๋ lock์ ๊ฑธ์ด์ฃผ๋ ๊ฒ์ด ์ข์ผ๋ฏ๋ก, ๋ณ๋๋ก filesys_write
์ด๋ผ๋ file_write
wrapper function์ ๊ตฌํํ์ฌ ์ฌ์ฉํ๋ค. do_file_io
๋ page์ ๋ํด ํด๋น ํ์ด์ง์ head-page*๋ฅผ ์ฐพ์์ ์ธ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋ ํจ์๋ฅผ ์ํํ๋ค. disk์ ์ ๊ทผํ๋ฏ๋ก ์ฌ๊ธฐ์๋ ๋ฝ์ ํ๋ฒ ๋ ๊ฑด๋ค.
/* Calculate page offset in mapped area
* and execute func handed by argument. */
static bool do_file_io(struct page *page, struct page *head, file_io func) {
struct file *file = head->file.file;
off_t offset = page->file.offset;
size_t length = page->file.length;
void *map_addr = page->file.map_addr;
void *kva = page->frame->kva;
ASSERT(page->frame && kva)
/* Calculate page offset. Starts from 1. */
off_t page_offset = (page->va - map_addr) / PGSIZE;
/* Calculate read-write-start point. */
off_t start = offset + page_offset * PGSIZE;
/* Calculate read-write-bytes. */
filesys_seek(file, start);
/*
offset len page-aligned
+----------+----------+----------+------+---+
| | | | | |
| | | | | |
| page | page | page | page |
| | | | | |
| | | | | |
+----------+----------+----------+------+---+
+----------+-------+
| |
| file |
| |
+----------+-------+
Take min(PGSIZE, (off+len)-rw_start, file_left) as rw_bytes of this page.
Page 0 takes PGSIZE,
Page 1 takes file_left, and so on.
*/
off_t file_left = filesys_length(file) - file->pos;
size_t bytes = (offset + length) - start;
bytes = bytes < PGSIZE ? bytes : PGSIZE;
bytes = bytes < file_left ? bytes : file_left;
/* Do func. */
if (func(file, kva, bytes) != (int)bytes) {
return false;
}
return true;
}
filesys_write
๋๋ filesys_read
์ ๋ฐํ๊ฐ์ธ byte๋ฅผ ๊ธฐ์ค์ผ๋ก ์๋ํ๋ ๋งํผ ํ์ผ ์ฝ๊ธฐ/์ฐ๊ธฐ๊ฐ ์ ์ํ๋์๋์ง ํ์ธํ๊ธฐ ์ํด ์ ํํ ๋ฐ์ดํธ์๋ฅผ ๊ณ์ฐํด์ฃผ์๋ค. ํํ ์ค ํ๋ก์ ํธ 3๊น์ง๋ ํ์ผ์ด grow ํ ์ ์๊ธฐ ๋๋ฌธ์ ํ์ผ ๊ธธ์ด, ํ์ด์ง ์ฌ์ด์ฆ, length ์ค์์ ๊ฐ์ฅ ์์ ๊ฐ์ ํํด io function์ ์ ๋ฌํ๋ค.
mmap์ ๊ตฌํํ๋ฉด์ ๊ฐ์ฅ ๊น๋ค๋ก์ ๋ ์ ์ค ํ๋๋ ์ฌ๋ฌ ํ์ด์ง์ ๊ฑธ์ณ ๋งคํ๋ ํ์ผ์ ๋ค๋ฃจ๋ ์ผ์ด์๋ค. ์ด๋ก ์ธํด ๋งคํ์ด ์์๋๋ ํ์ด์ง์ธ head-page์ ์ด์ ๋ธ๋ฆฐ ํ์ด์ง๋ค์ธ sub-pages ๊ฐ๋ ์ด ์๊ฒผ๋ค. ๊ตฌํ ๋ฐฉํฅ์ ๋ค์๊ณผ ๊ฐ๋ค.
- file-backed page๊ฐ ๋งคํ์ด ํด์ ๋๋ ๊ฒฝ์ฐ, ํด๋น ํ์ด์ง์ head-page๋ฅผ spt์์ ์ฐพ๋๋ค.
- head-page์์ ๋งคํ๋ ํ์ผ ์ ๋ณด๋ฅผ ์ฝ์ด์จ๋ค.
- ํ์ผ์ writeback์ ์ํํ ๋ค์ ํ์ด์ง๋ฅผ swap-outํ๊ฑฐ๋ destroyํ๋ค.
์ด๋ ๊ฒ ๊ตฌํํ ์ด์ ๋, ํ๋์ ๋งคํ์ ์ํ ์ฌ๋ฌ ํ์ด์ง๋ค์ด ๋ชจ๋ ํ์ผ์ ์ฌ๋ณธ (๊ฐ์ inode์ง๋ง file pointer๊ฐ ๋ค๋ฅธ)์ ๊ฐ์ง์ง ์๊ฒ ํ๋ฉด์๋, ํ๋์ file pointer์ ๋ํด filesys_close
๋ฅผ ๋ ๋ฒ ์ด์ ํธ์ถํ๋ ๊ฒ์ ๋ง๊ธฐ ์ํด์์ด๋ค. sub-pages๋ file pointer๋ฅผ ๊ฐ์ง ์๊ณ , head-page์ ๊ฐ์์ฃผ์ ์ ๋ณด๋ฅผ ๊ฐ๋๋ค.
// ํ์ด์ง ๊ตฌ์กฐ์ฒด ๋ด ํ์ผ ํ์ด์ง
struct file_page {
size_t slot;
off_t offset;
size_t length;
void *map_addr;
struct file *file;
};
/* Get head page of this mapping. */
static struct page *spt_get_head(struct page *page) {
struct thread *curr = thread_current();
/* Get file page struct. */
struct file_page *file_page = get_file_page(page);
if (is_file_head(page, file_page)) {
return page;
}
return spt_find_page(&curr->spt, file_page->map_addr);
}
/* Hash action function which write all file-backed pages
* back to disk if page is dirty.
* See supplemental_page_table_kill in vm.c */
void spt_file_writeback(struct hash_elem *e, void *aux) {
struct page *page = hash_entry(e, struct page, table_elem);
if (page_get_type(page) != VM_FILE) return;
if (!is_file_head(page, get_file_page(page))) return;
/* If page is head-page of
* file-backed pages, write back. */
file_write_back_all(page);
}
/* Write back all file-backed pages starting from head-page. */
static void file_write_back_all(struct page *head) {
struct thread *curr = thread_current();
void *p = head->va;
void *map_addr = head->va;
struct page *page = head;
while (page && get_file_page(page)->map_addr == map_addr) {
if (!pg_present(page)) {
/* Clearing uninit page will be handled by uninit_destroy.
* If swap-out page, no need to write back.
* Just go to next loop. */
p += PGSIZE;
page = spt_find_page(&curr->spt, p);
continue;
}
/* Write back to file. */
file_write_back(page, head);
p += PGSIZE;
page = spt_find_page(&curr->spt, p);
}
/* Close file. */
filesys_close(get_file_page(head)->file);
}
munmap
์์คํ
์ฝ์ด ํธ์ถ๋ ๋๋ ์ธ์๋ก ๋ฐ๋ ๊ฐ์์ฃผ์๊ฐ head-page์ ๊ฐ์์ฃผ์์ฌ์ผ ํ๋ค๋ ์ ์ฝ์ด ์๊ธฐ ๋๋ฌธ์ ํด๋น ์ฃผ์์ ๋ํด file_write_back_all
๋ง ํธ์ถํด์ค ํ, spt_remove_page
๋ก ํ์ด์ง๋ฅผ destroyํด์ฃผ๋ฉด ๋๋ค. ๋ค๋ง ํด์ ํ
์ด๋ธ์ ๋๋ฉด์ spt ์ ์ฒด๋ฅผ killํ๋ ๊ฒฝ์ฐ์๋ spt๋ฅผ ์ญ์ ํ๊ธฐ ์ ์ ๋จผ์ ๊ฐ file-backed page์ head-page๋ฅผ ๊ณจ๋ผ file_write_back_all
์ ํ๋ฒ์ฉ ํธ์ถํด์ผ ํ๋ค. (์์ spt_file_writeback
ํจ์๊ฐ ํด์์ ๊ฐ ์ํธ๋ฆฌ์ ๋ํด ์ํ๋๋ค.)
mmap
, munmap
์์ trickyํ ๋ถ๋ถ์ ์ด์ ๋์๋ ๊ฒ ๊ฐ๋ค. ์ฒ์์๋ destroy function ์์์ file writeback์ ์ํํ๋ ค๊ณ ํ์ผ๋ spt ํ
์ด๋ธ์ ์กฐ์ํ๋ฉด์ head-page๋ฅผ ์ฐพ๋ค ๋ณด๋ ์ค๊ฐ์ head๊ฐ ๋ ์๊ฐ๋ฒ๋ฆฌ๋ ๋ถ์์ฌ๊ฐ ์๊ฒผ๋ค. ๋ฐ๋ผ์ writeback์ writeback๋๋ก, remove๋ remove๋๋ก ์ํํ๋๋ก ๋ก์ง์ ๋ณ๊ฒฝํ๋ค.
/* Free the resource hold by the supplemental page table. */
void supplemental_page_table_kill(struct supplemental_page_table *spt) {
if (!spt) return;
/* Write back file_backed_pages first, */
hash_apply(&spt->hash, spt_file_writeback);
/* Destroy hash. */
hash_destroy(&spt->hash, spt_free_page);
return;
}
ํ๊ฐ์ง ๋ ์ ๊ฒฝ ์ผ๋(?) ๋ถ๋ถ์ ํจ์ ํฌ์ธํฐ์ ์ฌ์ฉ์ผ๋ก, filesys_read
, filesys_write
์ ์ธํฐํ์ด์ค๊ฐ ๋์ผํ๋ค๋ ์ ์ ํ์ฉํด ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์ ์ํํ๋ do_file_io
ํจ์๋ฅผ ๊ตฌํํด ์ค๋ณต๋๋ ๋ก์ง์ ์ค์ผ ์ ์์๋ค. ์ ์ฌํ๊ฒ, anon page swap in/out์์ swap disk io์ชฝ๋ ๋์ผํ ๋ฐฉ์์ ์ฌ์ฉํ๋ค. ๋ณ๊ฒฝํ๋ ๊น์ syscall ์ชฝ๋ ๋ชจ๋ ํจ์ ํฌ์ธํฐ๋ฅผ ์ฌ์ฉํ syscall table๋ก ์์ ํด๋์๋ค. ์ด๋ฐ ์์ผ๋ก ๋ก์ง์ ๋ฐ๋ก ๋นผ ๋๋ฉด, ์ผ๋จ 1. ์์ ํ๊ธฐ ํธํ๊ณ 2. ๊ณต์ ์์์ธ ๊ฒฝ์ฐ lock์ด๋ semaphore๋ฅผ ๊ฑธ๊ธฐ ์์ํด์ง๋ ๊ฒ ๊ฐ๋ค.
๐ ์์ฑ๋ mmap
& munmap
/* Do the mmap */
void *do_mmap(void *addr, size_t length, int writable, struct file *file,
off_t offset) {
struct thread *curr = thread_current();
/* If addr or offset is not page-aligned. */
if ((uint64_t)addr % PGSIZE || offset % PGSIZE) {
return NULL;
}
/* If addr is in spt. */
for (void *p = addr; p < addr + length; p += PGSIZE) {
if (spt_find_page(&curr->spt, p)) {
return NULL;
}
}
/* If addr in stack area. */
if (addr + length > STACK_LIMIT && addr < USER_STACK) {
return NULL;
}
/* Allocate page with lazy loading. */
struct file *mmap_file = file_duplicate(file);
long left = (long)length;
int cnt = 0;
while (left > 0) {
/* Save infos for initializing file-backed page. */
struct file_page *aux = calloc(1, sizeof(struct file_page));
/* Only head page holds mmap_file address. */
aux->file = cnt == 0 ? mmap_file : NULL; /* map file from */
aux->offset = offset; /* offset, */
aux->length = length; /* to length bytes. */
aux->map_addr = addr; /* va where mapping starts. */
if (!vm_alloc_page_with_initializer(VM_FILE, addr + (cnt * PGSIZE),
writable, lazy_load_file, aux)) {
return NULL;
}
left -= PGSIZE;
cnt++;
}
return addr;
}
/* Do the munmap. Unmap can be called when
* head or sub pages are not initialized. */
void do_munmap(void *addr) {
struct thread *curr = thread_current();
struct page *page = spt_find_page(&curr->spt, addr);
/* If page is not found, return. */
if (page == NULL) return;
/* Virtual address must be the map_addr. */
struct file_page *file_page = get_file_page(page);
if (file_page->map_addr != addr) return;
size_t length = file_page->length;
/* Write back all pages starting from head. */
file_write_back_all(page);
/* Remove head-page and all sub-pages from spt and pml4. */
for (void *p = addr; p < addr + length; p += PGSIZE) {
page = spt_find_page(&curr->spt, p);
spt_remove_page(&curr->spt, page);
}
}
/* Initialize file-backed frame. */
static bool lazy_load_file(struct page *page, void *aux) {
ASSERT(page->operations->type == VM_FILE)
ASSERT(page->frame)
/* Get head-page. */
struct thread *curr = thread_current();
struct page *head = spt_get_head(page);
ASSERT(head != NULL)
/* Read file to page. */
bool succ;
lock_acquire(&load_lock);
succ = do_file_io(page, head, file_read);
lock_release(&load_lock);
return succ;
}
05) Swap in/out
์ ์ ํ์์ ํ๋ ์์ ํ ๋น๋ฐ์์ค๋ ํ์ฌ์ vm_get_frame
ํจ์๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ด ๋ฐ๋ฅ๋ ๊ฒฝ์ฐ ํ๋ ์์ ๋ฐํํ์ง ๋ชปํ๋ค๋ ๋ฌธ์ ๊ฐ ์๋ค. ํ๋ก์ธ์ค๋ ์ปดํจํฐ๋ฅผ ์ถ์ํํ๋ฏ๋ก, ๋ฉ๋ชจ๋ฆฌ ์ญ์ ๋
๋ฆฝ์ ์ธ ๊ฐ์์ฃผ์๊ณต๊ฐ์ฒ๋ผ ์ฌ์ฉํ ์ ์์ด์ผ ํ๋ค. ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ฌ๋งํด์ ๋ฐ๋ฅ๋์ง ๋ชปํ๋๋ก swap disk๋ฅผ ์ฌ์ฉํด๋ณด์.
- ๋ฉ๋ชจ๋ฆฌ์์ ํ๋ ์์ ์ค์ ๋์คํฌ๋ก ๋ด๋ฆฌ๋ ๊ฒ์ swap-out์ด๋ผ๊ณ ํ๋ค.
- ์ค์ ๋์คํฌ์ ์๋ ํ๋ ์์ ๋ค์ ๋ฉ๋ชจ๋ฆฌ๋ก ์ฝ์ด ์ค๋ ๊ฒ์ swap-in์ด๋ผ๊ณ ํ๋ค.
static struct frame *vm_get_frame(void) {
struct frame *frame = NULL;
frame = calloc(1, sizeof(struct frame));
if (frame == NULL) {
printf("Frame allocation failed.\n");
return NULL;
}
/* Get anonymous frame. */
frame->kva = palloc_get_page(PAL_USER | PAL_ZERO);
if (frame->kva == NULL) {
free(frame);
/* If out of memory,
* evict frame from frame table.*/
frame = vm_evict_frame();
}
/* Init page list, push into frame table. */
list_init(&frame->pages);
list_push_back(&frame_table, &frame->elem);
ASSERT(frame != NULL);
ASSERT(list_empty(&frame->pages));
return frame;
}
์์ ๋ vm_get_frame
ํจ์๋ palloc_get_page(PAL_USER | PAL_ZERO);
๊ฐ ์คํจํ ๊ฒฝ์ฐ, ํ๋ ์ ํ
์ด๋ธ์์ ํ๋ ์์ ํ๋ swap-outํ๊ณ ๊ฐ์ ธ์จ๋ค. ์ฆ ๋จ์ด ์ฐ๋ ์ค๊ณ ์ธ ๊ฒ์ด๋ค.. ๋ฐ๋ผ์ evict_frame์ ํ ๋ ๋ค์๊ณผ ๊ฐ์ด ํ๋ ์์ ๊นจ๋์ด 0์ผ๋ก ๋ฐ์ด์ค์ผ ํ๋ค.
/* Evict one page and return the corresponding frame.
* Return NULL on error.*/
static struct frame *vm_evict_frame(void) {
struct frame *victim = vm_get_victim();
ASSERT(!list_empty(&victim->pages));
struct list_elem *front = list_front(&victim->pages);
struct page *page = list_entry(front, struct page, frame_elem);
/* Remove all pages from frame and
* push into swap table. */
if (swap_out(page)) {
/* After, initialize victim. */
ASSERT(list_empty(&victim->pages));
memset(victim->kva, 0, PGSIZE); // ๐ฅ 0์ผ๋ก ๋ฆฌ์
return victim;
}
return NULL;
}
์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด์๋ ๊ฐ ํ๋ก์ธ์ค๋ค์ ํ ๋น๋ ํ๋ ์์ ์ฃผ์๋ฅผ ์ฎ์ด ๋์ ์ ์ญ์ ์ธ frame table์ด ํ์ํ๋ค. ํ๋ ์ ํ ์ด๋ธ์ ์๋ฃ๊ตฌ์กฐ๋ ์ค์ ์์ํ ํ๋ ์์ ๊ณ ๋ฅด๋ ๋ฐฉ๋ฒ, ์ฆ evict policy์ ์ํด ๊ฒฐ์ ๋ ์ ์๋ค. ์ฐ๋ฆฌ ์กฐ๋ ๋จ์ FIFO๋ก ํ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ํ ํ๋ฅผ ํ์ฉํ Clock Algorithm์ผ๋ก ํ๋ฒ ๊ตฌํํด๋ณด์๋ค.
// ์ ์ญ๋ณ์
static struct list_elem *clock_hand;
static struct list frame_table;
static struct lock frame_lock;
void vm_init(void) {
vm_anon_init();
vm_file_init();
#ifdef EFILESYS /* For project 4 */
pagecache_init();
#endif
register_inspect_intr();
/* DO NOT MODIFY UPPER LINES. */
/* Used when each process access frame table. */
lock_init(&frame_lock);
/* Create frame table as CLL. */
list_init(&frame_table);
clock_hand = &frame_table.head;
frame_table.head.prev = &frame_table.tail;
frame_table.tail.next = &frame_table.head;
}
ํํ ์ค์ <list.h>
๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณต๋๋ ๋ฆฌ์คํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด์ค ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ด๊ธฐ ๋๋ฌธ์ head์ tail๋ง ์ฐ๊ฒฐํด์ฃผ๋ฉด ์ํ ํ๋ก ์ฌ์ฉํ ์ ์๋ค. Clock ์๊ณ ๋ฆฌ์ฆ์ ๊ธฐ๋ณธ ์์ด๋์ด๋ ๋ค์๊ณผ ๊ฐ๋ค.
- Clock hand๊ฐ ์ํ ํ๋ฅผ ๋๋ฉด์ ํ๋ ์๊ณผ ์ฐ๊ฒฐ๋ ํ์ด์ง์ ์ ๊ทผ ๋นํธ access bit๋ฅผ ๊ฒ์ฌํ๋ค.
- ์ ๊ทผ ๋นํธ๊ฐ 1๋ก ์ค์ ๋ ๊ฒฝ์ฐ, 0์ผ๋ก ์ค์ ํ๊ณ ๋ค์์ผ๋ก ๋์ด๊ฐ๋ค.
- ์ ๊ทผ ๋นํธ๊ฐ 0์ผ๋ก ์ค์ ๋ ๊ฒฝ์ฐ, ํด๋น ํ๋ ์์ victim์ผ๋ก ์ ์ ํ๊ณ ์ฆ์ ๋ฐํํ๋ค.
Clock์ LRU (Least Recently Used)๋ฅผ ๊ทผ์ฌํ ๋ฐฉ์์ผ๋ก ์ฌ๊ฒจ์ง๋๋ฐ, Clock hand๊ฐ ๋ฆฌ์คํธ๋ฅผ ํ ๋ฐํด ๋๋ ๋์ ์ ๊ทผ ๋นํธ๊ฐ 0์ธ, ์ฆ ์ ๊ทผ๋์ง ์์ ํ์ด์ง๋ฅผ '์ต๊ทผ์ ์ฌ์ฉ๋์ง ์์' ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค๋ ์ ์์ ๊ทธ๋ ๋ค. ๋ง์ฝ ์ ๊ทผ ๋นํธ๊ฐ 1์ธ ๊ฒฝ์ฐ 0์ผ๋ก ์ธํ ํ๊ณ ๋ค์์ผ๋ก ๋์ด๊ฐ๋ฏ๋ก Second-chance๋ฅผ ์ฃผ๋ ์๊ณ ๋ฆฌ์ฆ์ด๋ผ๊ณ ๋ณผ ์๋ ์๋ค. ์ด๋ ๊ฐ์ฅ ์ต๊ทผ์ ์ฐธ์กฐ๋์๋ ํ์ด์ง๊ฐ ์์ผ๋ก๋ ๊ฐ์ฅ ๋นจ๋ฆฌ ๋ค์ ์ฐธ์กฐ๋ ๊ฐ๋ฅ์ฑ์ด ๋๋ค๋ ์ฐธ์กฐ์ง์ญ์ฑ ๊ฐ๋ ์ ๊ธฐ๋ฐํ๋ค. (๋ฐ๋ณต๋ฌธ ๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด๋ณด์.)
Test์์๋ ๋ณดํต ์ฌ๋ฌ ํ์ด์ง๋ฅผ ์์ฐจ์ ์ผ๋ก ๋จ ํ๋ฒ์ฉ๋ง ์ ๊ทผํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ FIFO์ Clock์ ์ฑ๋ฅ์ด ์ ์๋ฏธํ ์ฐจ์ด๋ฅผ ๋ณด์ด์ง ์์๋ ๊ฒ ๊ฐ์ง๋ง, ๋งคํ๋ ์ผ๋ถ ํ์ด์ง๋ง ๋ฐ๋ณต์ ์ผ๋ก ์ ๊ทผํ๊ฑฐ๋ ๋๋คํ ์์๋ก ์ ๊ทผํ ๊ฒฝ์ฐ Clock์ ์ฑ๋ฅ์ด ๋์์ง๋ ๋ชจ์ต์ ๋ณผ ์ ์์๋ค.
/* Get the struct frame, that will be evicted. */
static struct frame *vm_get_victim(void) {
struct list_elem *head = &frame_table.head;
struct list_elem *tail = &frame_table.tail;
if (list_empty(&frame_table)) {
PANIC("No entry in frame table.");
}
lock_acquire(&frame_lock);
struct list_elem *curr = clock_hand;
size_t frame_access = SIZE_MAX;
size_t least_access = SIZE_MAX;
struct frame *frame = NULL;
struct frame *victim = NULL;
/* Clock algorithm with CLL */
while (frame_access != 0) {
if (curr == head || curr == tail) {
curr = list_next(curr);
continue;
}
frame = list_entry(curr, struct frame, elem);
frame_access = get_access_pages(frame);
clear_access_pages(frame);
if (frame_access < least_access) {
least_access = frame_access;
victim = frame;
}
curr = list_next(curr);
}
clock_hand = curr;
list_remove(&victim->elem);
lock_release(&frame_lock);
return victim;
}
๋ค์์ ์ปค์คํ ํ ์คํธ๋ก Clock๊ณผ FIFO๋ฅผ ๋๋ฆฐ ๊ฒฐ๊ณผ์ด๋ค. Tick์์ hd1:1(swap disk) ioํ์๊ฐ ์ ์๋ฏธํ ์ฐจ์ด๋ฅผ ๋ณด์ด๋ ๊ฒ์ ์ ์ ์๋ค.
๐ ๊ทธ๋ฌ๋ฉด FIFO๊ฐ Clock๋ณด๋ค ํญ์ ๋์ ๊ฑด ์๋๋ค?
๋ง์ฝ ์ด์์ฒด์ ๊ฐ ํ๋ก๊ทธ๋จ์ ๋ฉ๋ชจ๋ฆฌ ์ ๊ทผ ํจํด์ ๋ฏธ๋ฆฌ ์๊ณ ์๋ค๋ฉด ์ด์ ์ต์ ํ๋ ๊ต์ฒด ์๊ณ ๋ฆฌ์ฆ์ ํํ ์ ์์ ๊ฒ์ด๋ค. ์ ๊ทผ ํจํด์ด ๋ฐ๋ณต์ ์ด์ง ์๊ณ ์์ฐจ์ ์ด๋ผ๋ฉด, clock์ ํ์ํ ์ฌ๋ฌ ๋ฃจํด๋ค (pml4์์ ์ ๊ทผ ๋นํธ ์ฝ์ด์ค๊ธฐ, ๋ฆฌ์คํธ ์ํํ๊ธฐ..) ์ ์ค๋ฒํค๋๋ก ๋จ๊ฒ ๋๋ค. ๋ฆฌ๋ ์ค์์ ์ฌ์ฉ๋๋ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ํด์ clock์ด ํญ์ ๋น ๋ฅธ ๊ฒ ์๋์ง ์๊ฐํ์๋๋ฐ, ๋ง์ ํ ์คํธ๋ฅผ ๋๋ ค๋ณด๊ณ ๊ฒฐ๊ณผ๋ฅผ ๊ด์ฐฐํ๋ค๋ณด๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ด๋ค ์์ผ๋ก ์ ๊ทผ๋๋์ง์ ๋ฐ๋ผ ๊ฐ ๊ต์ฒด ์ ์ฑ ์ด ์ ๋ฆฌํ๊ฑฐ๋ ๋ถ๋ฆฌํ ๋๊ฐ ์๋ ๊ฒ ๊ฐ๋ค. ๋ง์ฝ ํ๋ก๊ทธ๋จ์ ์ ๊ทผ ํจํด์ ์์ธกํ ์ ์๊ฑฐ๋, FIFO ๋๋น ๋ณต์กํ Clock์ ๋ฃจํด์ด FIFO์ ๋จ์ ์ ์ปค๋ฒํ ๊ฒฝ์ฐ, Clock ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ ์ ์๊ฒ ๋ค.
// CLOCK.
Executing 'swap-iter':
(swap-iter) begin
(swap-iter) open "large.txt"
(swap-iter) mmap "large.txt"
(swap-iter) end
swap-iter: exit(0)
Execution of 'swap-iter' complete.
Timer: 9237 ticks
Thread: 30 idle ticks, 216 kernel ticks, 8992 user ticks
hd0:0: 0 reads, 0 writes
hd0:1: 43158 reads, 15934 writes
hd1:0: 7948 reads, 0 writes
hd1:1: 281224 reads, 320328 writes
// FIFO
Executing 'swap-iter':
(swap-iter) begin
(swap-iter) open "large.txt"
(swap-iter) mmap "large.txt"
(swap-iter) end
swap-iter: exit(0)
Execution of 'swap-iter' complete.
Timer: 9398 ticks
Thread: 30 idle ticks, 212 kernel ticks, 9156 user ticks
hd0:0: 0 reads, 0 writes
hd0:1: 43158 reads, 15934 writes
hd1:0: 7948 reads, 0 writes
hd1:1: 281800 reads, 320904 writes
ํ์ด์ง ๊ต์ฒด ์ ์ฑ
์ ์ ์ ํ์ผ๋ฉด ์ด์ swap in/out์ ๊ตฌํํด์ผ ํ๋ค. anon page์ file-backed page ๋ชจ๋ ๋ฃจํด์ ๋์ผํ๋ฉฐ, ๊ฐ๊ฐ ์ค์ ๋์คํฌ์ ํ์ผ์ ์จ์ค๋ค๋ ๊ฒ๋ง ๋ค๋ฅด๋ค. anon์ ๊ฒฝ์ฐ ์ค์ ๋์คํฌ์ ์ด๋ค ์นํฐ์ ๊ธฐ๋กํ๋์ง๋ฅผ ์์์ผ ๋์ค์ ๋ค์ swap inํ ์ ์์ผ๋ฏ๋ก, anon_page
๊ตฌ์กฐ์ฒด ์์ ์ฌ๋กฏ ๋๋ฒ๋ฅผ ๊ธฐ๋กํด๋๋ค. ๋์คํฌ ์นํฐ์ ๊ฐ์ฉ ์ํ๋ง ๊ด๋ฆฌํ๋ฉด ๋๋ฏ๋ก, bitmap์ ์ฌ์ฉํ ์ ์๋ค.
/* Swap table. */
#define DISK_SEC 512
#define SEC_PER_PAGE 8
#define MAX_SLOTS 32768
#define SLOT_INIT (size_t)(-1)
static struct bitmap *swap_slot;
static struct page **swap_table; // used for copy-on-write.
/* Semaphore for sector allocation. */
static struct lock slot_lock;
static struct lock disk_lock;
/* Initialize the data for anonymous pages */
void vm_anon_init(void) {
/* Set up the swap_disk. */
swap_disk = disk_get(1, 1);
/* Calculate disk size. */
size_t slots = disk_size(swap_disk) / SEC_PER_PAGE;
slots = MAX_SLOTS < slots ? MAX_SLOTS : slots;
/* Create swap slot table. */
swap_slot = bitmap_create(slots);
/* Create swap page table: copy-on-write. */
size_t page_cnt = (slots * sizeof(struct page *)) / 0x1000;
swap_table = palloc_get_multiple(PAL_ZERO, page_cnt);
/* Initialize locks. */
lock_init(&slot_lock);
lock_init(&disk_lock);
}
/* Find free disk slot. */
static size_t allocate_slot() {
lock_acquire(&slot_lock);
size_t slot = bitmap_scan_and_flip(swap_slot, 0, 1, false);
lock_release(&slot_lock);
return slot;
}
/* Mark as free slot. */
static void free_slot(size_t slot) {
lock_acquire(&slot_lock);
bitmap_set(swap_slot, slot, false);
lock_release(&slot_lock);
}
์ฌ๋ฌ ํ๋ก์ธ์ค๊ฐ ์ค์ ํ
์ด๋ธ์ ๊ณต์ ํ๋ฏ๋ก ๋ฝ์ ๊ฑธ์ด ๋น ์ฌ๋กฏ์ ํ ๋น/ํด์ ํ๋ ์์ผ๋ก ๊ตฌํํด์ฃผ์๋ค. swap_table
์ copy-on-write์ ๊ตฌํํ์ง ์๋๋ค๋ฉด ๋น์ฅ์ ํ์์๋ค.
/* Swap out the page by writing contents to the swap disk. */
static bool anon_swap_out(struct page *page) {
struct frame *frame = page->frame;
void *kva = page->frame->kva;
ASSERT(frame && kva)
/* Find free disk sector */
size_t slot = allocate_slot();
if (slot == BITMAP_ERROR) {
printf("Swap disk out of memory.\n");
return false;
}
/* Write to swap disk. */
disk_sector_t sec = slot * SEC_PER_PAGE;
do_disk_io(sec, SEC_PER_PAGE, kva, disk_write);
/* Clear all linked pages. */
struct thread *t;
struct page *prev;
struct list_elem *front;
while (!list_empty(&frame->pages)) {
front = list_pop_front(&frame->pages);
page = list_entry(front, struct page, frame_elem);
t = page->thread;
/* Unlink with frame. */
page->frame = NULL;
page->flags = page->flags & ~PTE_P;
pml4_set_accessed(t->pml4, page->va, false);
pml4_clear_page(t->pml4, page->va);
/* Link with disk slot. */
page->anon.slot = slot;
swap_table_push(slot, page);
ASSERT(!swap_table_empty(slot));
}
return true;
}
/* Swap in the page by read contents from the swap disk. */
static bool anon_swap_in(struct page *page, void *kva) {
ASSERT(page_get_type(page) == VM_ANON)
struct frame *frame = page->frame;
ASSERT(frame && kva)
size_t slot = page->anon.slot;
if (slot == SLOT_INIT) {
/* If page was never swapped out, just install. */
list_push_back(&frame->pages, &page->frame_elem);
return vm_install_page(page, page->thread);
}
/* Read from disk_sec */
disk_sector_t sec = slot * SEC_PER_PAGE;
do_disk_io(sec, SEC_PER_PAGE, kva, disk_read);
/* Set this page's access bit. */
pml4_set_accessed(page->thread->pml4, page->va, true);
/* Set all linked pages present. */
while (!swap_table_empty(slot)) {
page = swap_table_pop(slot);
ASSERT(page != NULL);
/* Link with frame. */
page->frame = frame;
page->flags = page->flags | PTE_P;
vm_map_frame(page, frame);
vm_install_page(page, page->thread);
/* Unlink with disk slot. */
page->anon.slot = SLOT_INIT;
}
/* Mark as free sector. */
free_slot(slot);
return true;
}
๊ตฌํ๋ anon page์ swap in/out ์ ๋ค์๊ณผ ๊ฐ๋ค. ํ๋ ์ ์์ pages
๋ฆฌ์คํธ๊ฐ ์๋ ๋ถ๋ถ์ด ์์ํ ์ ์๋๋ฐ ์ด๋ copy-on-write๋ก ๋ถ๋ชจ์ ์์์ ํ์ด์ง๊ฐ ๋์ผํ ํ๋ ์์ ์ฐ๊ฒฐ๋์ด ์๋ ๊ฒฝ์ฐ ํ๋ ์:ํ์ด์ง๊ฐ ์ผ๋๋ค ๊ด๊ณ๊ฐ ๋๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ frame์ page์ ๋ฆฌ์คํธ๋ฅผ ๊ฐ๊ณ page๋ frame์ ์ฃผ์๋ฅผ ์ฐธ์กฐํ๋ค. ๊ทธ๋ฌ๋ฏ๋ก swap out๋๋ฉด, ํ๋ ์์ pages
๋ฆฌ์คํธ๋ฅผ ๋๋ฉด์ ํด๋น ํ์ด์ง๋ฅผ ๊ฐ๊ฐ์ thread->pml4
์์ ํด๋ฆฌ์ดํด์ฃผ๊ณ , ์ ๊ทผ ๋นํธ๋ฅผ false๋ก ์ค์ ํด์ค๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋น ํ์ด์ง๋ค์ swap_table
์ ๋ฐฑ์
ํด๋๋ค.
SLOT_INIT
๋งคํฌ๋ก๋ ์ด๊ธฐ anon page์ ์ฌ๋กฏ์ ์ด๊ธฐํํด์ค ๋ ๋ฃ๋ ๊ฐ์ธ๋ฐ, ์ด๋ ํนํ spt table์ ๋ณต์ฌํ๋ fork
๊ณผ์ (copy-on-write๋ผ๋ฉด vm_handle_wp
)์์ ์ค์ํ๋ค. ๋ถ๋ชจ์ page๊ฐ ์ด๋ฏธ anon์ผ๋ก ์ด๊ธฐํ๋ ๊ฒฝ์ฐ, ์์์ page์์ vm_do_claim_page
์ swap_in
์์ ๋ถ๋ฆฌ๋ ํจ์๋ uninit_swap_in
์ด ์๋๋ผ anon_swap_in
์ด๋ค. ์ด๋์ ๋ก์ง์ ๋ถ๋ฆฌํด์ฃผ๊ธฐ ์ํด slot
์ด SLOT_INIT
์ธ ์ํ์ธ์ง๋ฅผ ํ์ธํ๋ค.
๐ ์ด ์ฝ๋์์ swap_table์ ์๋ฃ๊ตฌ์กฐ?
๋จ๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ๊ตฌํํ๋ค. ์ฒ์์๋ <list.h>
์ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํด list
์ ๋ฐฐ์ด๋ก ๊ตฌํํ๋ ค๊ณ ํ๋๋ฐ, ๋ฆฌ์คํธ ๊ตฌ์กฐ์ฒด ํ๋๋น ์ฃผ์๊ฐ์ด 4๊ฐ๊ฐ ๋ค์ด๊ฐ๋ฏ๋ก 32๋ฐ์ดํธ๊ฐ ๋๋ค. ๊ทธ๋ฐ๋ฐ ์ด์ฐจํผ ์ค๊ฐ ์ญ์ ๊ฐ ๋ง์ง ์๊ณ , stack ์ฒ๋ผ ํ๋ฐฉํฅ์ผ๋ก push, pop์ ์ํํ ๊ฒ์ด๋ผ๋ฉด ๋จ๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ก ๊ตฌํํด๋ ๊ด์ฐฎ์ง ์์๊น ์ถ์๋ค. (swap-out์์๋ frame->pages
์ ์ํ ๋ชจ๋ ํ์ด์ง๋ฅผ ๋ค ๋ฝ์์ swap_table[slot]
์ผ๋ก ๋ณด๋ด๊ณ , swap-in์์๋ ๊ทธ ๋ฐ๋๋ก ํ๋ฏ๋ก..) ๊ทธ๋์ struct page
์์ struct page*
ํ์
์ next_swap
๋ฉค๋ฒ๋ฅผ ๋ฃ์ด ๋๊ณ , ๋จ๋ฐฉํฅ ์ฐ๊ฒฐ๋ฆฌ์คํธ์ ๋งํฌ๋ก ํ์ฉํ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์ฌ๋กฏ ํ๋๋น 32๋ฐ์ดํธ ๋์ 8๋ฐ์ดํธ๋ง ์ฐ๊ณ , ๋์ ํ์ด์ง ๊ตฌ์กฐ์ฒด ์์ struct page*
์ฃผ์๋ฅผ ๋ฃ์ด ๋๋ฏ๋ก ์ด 16๋ฐ์ดํธ๋ก.. ์ฝ ๋ ๋ฐฐ ์ ๋ ๊ณต๊ฐ์ ์ ์ฝํ๋ค.
+) ๊ทธ๋ฐ๋ฐ ์ฌ์ค ํ์ด์ง์ ์ํ๋ณ๋ก ๋์์ ์กด์ฌํ ์ ์๋ ํ
์ด๋ธ์ ์๊ฐํด๋ณด๋ฉด, swap-in ๋ ํ์ด์ง๋ ํ๋ ์์ ๋ฆฌ์คํธ์ ์ฎ์ฌ ์๊ณ , swap-out๋ ํ์ด์ง๋ ์ค์ ํ
์ด๋ธ์ ์ฎ์ฌ ์๋ค. ๋ฐ๋ผ์ ํ๋ ์ ๊ตฌ์กฐ์ฒด ์์ struct list pages
๋ ๋จ๋ฐฉํฅ ์ฐ๊ฒฐ๋ฆฌ์คํธ๋ก ๊ตฌํํด์ ์ฃผ์๊ฐ ํ๋๋ง ๊ฐ์ง๊ณ ์๋๋ก ๊ฐ์ ํ ์ ์์ ๊ฒ ๊ฐ๋ค.
ํ์ด์ง ์ํ | ํ ์ด๋ธ |
---|---|
uninitialized | spt |
swap-in | spt , frame->pages |
swap-out | spt , swap_table[slot] |
struct page {
const struct page_operations *operations;
void *va; /* Address in terms of user space */
struct frame *frame; /* Back reference for frame */
struct hash_elem table_elem; /* Hash elem for spt. */
struct list_elem frame_elem; /* List elem for frame-mapping. */
struct page* next_swap; /* ๐ฅ Singly lisked list for swap-table. */
struct thread* thread; /* Thread info. */
uint16_t flags; /* Flags. */
...
}
/* Push front into swap table. */
static void swap_table_push(size_t slot, struct page *page) {
ASSERT(slot < MAX_SLOTS)
lock_acquire(&slot_lock);
struct page *curr = swap_table[slot];
page->next_swap = curr;
swap_table[slot] = page;
lock_release(&slot_lock);
}
/* Pop front from swap table. */
static struct page *swap_table_pop(size_t slot) {
ASSERT(slot < MAX_SLOTS)
lock_acquire(&slot_lock);
/* Returns NULL if table is empty. */
struct page *top = swap_table[slot];
if (top != NULL) {
swap_table[slot] = top->next_swap;
top->next_swap = NULL;
}
lock_release(&slot_lock);
return top;
}
/* Returns if swap table is empty. */
static bool swap_table_empty(size_t slot) {
ASSERT(slot < MAX_SLOTS)
bool rtn;
lock_acquire(&slot_lock);
rtn = (swap_table[slot] == NULL);
lock_release(&slot_lock);
return rtn;
}
/* Remove page from swap table. */
static struct page *swap_table_remove(size_t slot, struct page *page) {
ASSERT(slot != SLOT_INIT);
lock_acquire(&slot_lock);
struct page *before = swap_table[slot];
if (before == page) {
lock_release(&slot_lock);
return swap_table_pop(slot);
}
while (before->next_swap != page) {
before = before->next_swap;
if (before == NULL) {
/* Page is not found. */
lock_release(&slot_lock);
return NULL;
}
}
before->next_swap = page->next_swap;
lock_release(&slot_lock);
return page;
}
๋จ์ ์ด๋ผ๋ฉด, swap out๋ ํ์ด์ง๊ฐ destroy ๋๋ ๊ฒฝ์ฐ๊ฐ ์๊ธด๋ค. ๊ทธ๋ฌ๋ฉด swap_table[slot]
๋ฆฌ์คํธ์์๋ ์ญ์ ํด ์ฃผ์ด์ผ ํ๋๋ฐ.. SLL์ด๋ผ ์ค๊ฐ ์ญ์ ์ ๊ฒฝ์ฐ ๋ฆฌ์คํธ๋ฅผ ํ๋ฒ ๊ฒ์ํด ์ฃผ์ด์ผ ํ๋ค. ์ด๋ $O(n)$์ ์๊ฐ์ด ๊ฑธ๋ฆฐ๋ค. ์ฌ๊ธฐ์ n์ ํ๋์ ํ๋ ์์ ๊ณต์ ํ๊ณ ์๋ ํ์ด์ง์ ์์ด๋ค.
06) Copy-on-write
์ด๋ ต๋ค.. ์ด๋ ค์ด๋ฐ? .. ์ด๋ ต๋ค..
์์ด๋์ด๋ ๋ถ๋ชจ์ ์์์ ํ์ด์ง ๋ณต์ฌ ์ญ์ lazyํ๊ฒ ํ๊ฒ ๋ค๋ ๊ฑฐ๋ค. fork
์ฝ๋ก ์ธํด ์ฒ์ ํ์ด์ง ํ
์ด๋ธ์ ๋ณต์ฌํ ๋, ๋ถ๋ชจ์ ์์์ ํ์ด์ง๋ ๊ฐ์ ํ๋ ์๊ณผ ๋งคํ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๋ ํ์ชฝ์ด ํ๋ ์์ ์์ ํ๋ ค๊ณ ํ ๋ ๋น๋ก์ ์๋ก์ด ํ๋ ์์ด ํ ๋น๋์ด ๋ณต์ ๋๋ค. ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด write-protect page์ fault๋ฅผ ํ์ฉํ ์ ์๋ค.
- spt copy์์ ๋ถ๋ชจ์ ์์์ ํ์ด์ง๋ฅผ read-only๋ก ์ค์ ํ๋ค.
- ๋ง์ฝ read-only ํ์ด์ง์ ๋ํ write fault๊ฐ ๋ฐ์ํ๋ฉด, ๋ณต์ ํ๋ค.
๐จ ์ฃผ์: ์์ง ๊ฐ์ ํด์ผ ํ ์ ์ด ๋ง์ ์ค๋ฅ์ฝ๋์
/* Hash action function which copies a single page. */
static void spt_copy_page(struct hash_elem *e, void *aux) {
struct supplemental_page_table *dsc_spt =
(struct supplemental_page_table *)aux;
struct hash *dsc_hash = &dsc_spt->hash;
struct page *src_page = hash_entry(e, struct page, table_elem);
struct page *dsc_page = calloc(1, sizeof(struct page));
if (dsc_page == NULL) {
printf("Child page allocation failed\n");
return;
}
/* Copy spt entries. */
memcpy(dsc_page, src_page, sizeof(struct page));
/* Disconnect from parent's hash table. */
memset(&dsc_page->table_elem, 0, sizeof(struct hash_elem));
memset(&dsc_page->frame_elem, 0, sizeof(struct list_elem));
hash_insert(dsc_hash, &dsc_page->table_elem);
/* Set new values. */
dsc_page->next_swap = NULL;
dsc_page->thread = thread_current();
switch (page_get_type(src_page)) {
case VM_ANON:
if (!pg_present(src_page)) {
if (!pg_init(src_page)) {
/* Uninitialized anon pages are segment pages. */
struct file_info *src_aux = src_page->uninit.aux;
struct file_info *dsc_aux = calloc(1, sizeof(struct file_info));
if (dsc_aux == NULL) {
printf("Child uninit page aux allocation failed\n");
return;
}
memcpy(dsc_aux, src_aux, sizeof(struct file_info));
dsc_page->uninit.aux = dsc_aux;
return;
}
/* Swapped out anon page. */
size_t slot = dsc_page->anon.slot;
anon_swap_table_push(slot, dsc_page);
} else {
/* Present anon page. */
vm_map_frame(dsc_page, src_page->frame);
}
break;
case VM_FILE:
if (!pg_present(src_page)) {
/* Uninitialized file-backed page. */
if (!pg_init(src_page)) {
struct file_page *src_aux = src_page->uninit.aux;
struct file_page *dsc_aux = calloc(1, sizeof(struct file_page));
if (dsc_aux == NULL) {
printf("Child uninit page aux allocation failed\n");
return;
}
memcpy(dsc_aux, src_aux, sizeof(struct file_page));
dsc_page->uninit.aux = dsc_aux;
spt_copy_file(src_page, dsc_page);
/* Return immediately. */
return;
}
/* Swapped out file-backed page. */
size_t slot = dsc_page->file.slot;
file_swap_table_push(slot, dsc_page);
} else {
/* Present file-backed page. */
vm_map_frame(dsc_page, src_page->frame);
}
/* Copy file if the page is head-page. */
spt_copy_file(src_page, dsc_page);
break;
default:
break;
}
/* Set write-protect. */
src_page->flags = src_page->flags & ~PTE_W;
dsc_page->flags = dsc_page->flags & ~PTE_W;
/* Set copy-on-write flag. */
src_page->flags = src_page->flags | PG_COW;
dsc_page->flags = dsc_page->flags | PG_COW;
/* Install in pml4 if swap-in pages. */
if (pg_present(src_page)) {
vm_install_page(src_page, src_page->thread);
vm_install_page(dsc_page, dsc_page->thread);
}
}
๊ธธ๋ค.. case๋ฌธ 2๊ฐ๋ ํ๋ก์ ํธ 3์ ์ฃผ์ ๋ ๊ฐ ๋ฉ๋ชจ๋ฆฌ ํ์
์ธ anon๊ณผ file๋ก ๋๋์ด์ ธ ์๊ณ , ๊ฐ๊ฐ์ case์์์ 1. uninit์ธ ๊ฒฝ์ฐ, 2. swap-out์ธ ๊ฒฝ์ฐ, 3. swap-in ์ธ ๊ฒฝ์ฐ๋ก ๋๋๋ค. in๋์ด ์๋ ํ์ด์ง์ ๊ฒฝ์ฐ์๋ installํ๋ฉด์ ๋นํธ๋ฅผ & ~PTE_W
์ผ๋ก ์ค์ ํด ์ค๋ค.
/* Page Fault Handler: Return true on success */
bool vm_try_handle_fault(struct intr_frame *f, void *addr, bool user,
bool write, bool not_present) {
...
/* If page is write-protect, return handle_wp. */
if (write && !pg_writable(page)) {
return vm_handle_wp(page);
}
...
}
/* Handle the fault on write_protected page */
// TODO: uninit page๋ handleํ๊ฒ ?
bool vm_handle_wp(struct page *page) {
if (!pg_copy_on_write(page)) {
/* If cow-flag is not on,
* this page is write-protect page. */
return false;
}
/* Unlink from old frame. */
struct frame *old_frame = page->frame;
vm_unmap_frame(page);
/* If this page was the last,
* Re-link with frame and return. */
if (list_empty(&old_frame->pages)) {
page->flags = page->flags | PTE_W;
page->flags = page->flags & ~PG_COW;
vm_map_frame(page, old_frame);
return vm_install_page(page, page->thread);
}
/* Link with new frame. */
struct frame *new_frame;
/* Copy-on-write pages are all present. */
if (pg_present(page)) {
/* Mark as no copy-on-write page. */
page->flags = page->flags | PTE_W;
page->flags = page->flags & ~PG_COW;
if (!vm_do_claim_page(page)) {
/* If swap-in fails, return false.*/
return false;
}
/* Page is now swapped-in. Copy old frame. */
new_frame = page->frame;
memcpy(new_frame->kva, old_frame->kva, PGSIZE);
return true;
}
return false;
}
PG_COW
๋ก ์ค์ ๋ ํ์ด์ง๋ ์์ ๊ฐ์ด vm_try_handle_fault
์ฒ๋ฆฌ ์ค ๋ณ๋๋ก ํธ๋ค๋งํ๋ค. ๊ทธ๋ฐ๋ฐ ์ง๊ธ ๋ณด๋ swap-out๋ ํ์ด์ง๋ ๋นํธ ์ค์ ์ ํด ์ฃผ์ด์ผ ํ ๊ฒ ๊ฐ๋ค.. copy-on-write์ ๊ฒฝ์ฐ extra ์ต๊ฐ์๋ต๊ฒ ๋์ด๋๊ฐ ๋์ ๊ฒ ๊ฐ๋ค. ๊ทธ๋ฆฌ๊ณ ์ง๊ธ๊น์ง์ ์๋ฃ๊ตฌ์กฐ๋ฅผ ๋ฐ๊ฟ์ผ ํด์ ๋ง๋ฐ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ๊ฐ ์ด๋ ค์ ๋ค. (๋ฌผ๋ก ์ด๊ฒ๊น์ง ์ ํด๋ ๋์ถฉ cow-simple ํ
์คํธ๋ง ํต๊ณผํ๊ฒ๋ ์ฒ๋ฆฌํด๋ ์๋ ์๋ค.) ํ ๊ฐ์ง ๋ ์ด๋ ค์ ๋ ๋ถ๋ถ์: read-only ์ค์ ์ผ๋ก ์ธํด fault์๊ฐ ์ฆ๊ฐํ๋ฉด์ ์ด๊ณณ์ ๊ณณ์ ๊ฑธ์ด ๋์๋ ๋ฝ๊ณผ ์ธ๋งํฌ์ด๋ก ์ธํ ๋๊ธฐํ ๋ฌธ์ ๊ฐ ํฐ์ง๊ธฐ ์์ํ๋ค๋ ์ ์ด๋ค. ์ฝ๋์ ํ๋ฆ์ ์ ํํ๊ฒ ์๊ณ ์์ด์ผ ๊ฐ๊ธ์ ์ข์ ๋ฒ์์์, ํ์ํ ๋งํผ๋ง ๋ฝ์ ๊ฑธ์ด ์์ ์ ๊ทผ์ ์ ์ดํ ์ ์์ ๊ฒ ๊ฐ๋ค. ๊ธฐํ๊ฐ ๋๋ค๋ฉด ํ ๋ฟ๋ ๋ฐ๊น์ง ์กฐ๊ธ ๋ ๋ณด์ํ๊ณ ์ถ์ ์ ์ด๋ค.
'DevLog ๐จ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[CS][Study] Monitor & Conditional Variable (1) | 2024.01.11 |
---|---|
[DevLog][PintOS] 3์ฃผ๊ฐ์ ํํ ์ค ํ๊ณ (1) | 2023.10.24 |
[DevLog][PintOS] PRJ3 Virtual Memory/Memory Management to Stack Growth (2) | 2023.10.17 |
[DevLog][PintOS] PRJ2 User Program/Out of Memory Test (0) | 2023.10.10 |
[DevLog][PintOS] PRJ1 Threads/PintOS ์ค๋ ๋๋ ์ด๋ป๊ฒ ๋์๊ฐ๊น? (0) | 2023.10.03 |