Implement Supplemental Page Table
- PintOS์๋ pml4๋ผ๋ ํ์ด์ง ํ ์ด๋ธ์ด ์กด์ฌํ์ง๋ง Page Fault์ ์์ ๊ด๋ฆฌ๋ฅผ ์ํด ๋ ๋ง์ ์ ๋ณด๋ฅผ ๋ด์ Supplemental Page Table์ด ํ์ํ๊ณ ์ด๋ฅผ ๊ตฌํ
- ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ Frame๊ณผ Management์ ํ์ํ ํจ์ ๊ตฌํ
struct page {
const struct page_operations *operations;
void *va; /* Address in terms of user space */
struct frame *frame; /* Back reference for frame */
union {
struct uninit_page uninit;
struct anon_page anon;
struct file_page file;
#ifdef EFILESYS
struct page_cache page_cache;
#endif
};
};
ํ์ฌ include/vm/vm.h
์ page ๊ตฌ์กฐ์ฒด๋ ์์ ๊ฐ์ด ์ ์๋จ
Page Initialization with Lazy Loading
- Lazy Loading(= Demand Paging)์ ์ํด uninit ์ํ์ ํ์ด์ง ์์ฑ
- ๊ฐ์ ์ฃผ์ / ํ์ด์ง ํ์ / initializer ๋ฑ ํ๋ผ๋ฏธํฐ ์ค์ ํ uninit_new() ํธ์ถ
static bool
load_segment (struct file *file, off_t ofs, uint8_t *upage,
uint32_t read_bytes, uint32_t zero_bytes, bool writable) {
ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
ASSERT (pg_ofs (upage) == 0);
ASSERT (ofs % PGSIZE == 0);
while (read_bytes > 0 || zero_bytes > 0) {
/* Do calculate how to fill this page.
* We will read PAGE_READ_BYTES bytes from FILE
* and zero the final PAGE_ZERO_BYTES bytes. */
size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
size_t page_zero_bytes = PGSIZE - page_read_bytes;
/* TODO: Set up aux to pass information to the lazy_load_segment. */
struct dummy *aux = (struct dummy*)malloc(sizeof(struct dummy));
aux->file = file;
aux->read_bytes = page_read_bytes; // * ํ์ง
aux->zero_bytes = page_zero_bytes;
aux->ofs = ofs;
if (!vm_alloc_page_with_initializer (VM_ANON, upage, writable, lazy_load_segment, aux))
return false;
/* Advance. */
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
ofs += page_read_bytes;
upage += PGSIZE;
}
return true;
}
bool vm_alloc_page_with_initializer(enum vm_type type, void *upage, bool writable,
vm_initializer *init, void *aux)
{
ASSERT(VM_TYPE(type) != VM_UNINIT)
bool success = false;
struct supplemental_page_table *spt = &thread_current()->spt;
/* Check wheter the upage is already occupied or not. */
if (spt_find_page(spt, upage) == NULL)
{
/* TODO: Create the page, fetch the initializer according to the VM type,
* TODO: and then create "uninit" page struct by calling uninit_new. You
* TODO: should modify the field after calling the uninit_new. */
struct page *p = (struct page *)malloc(sizeof(struct page));
if (type == VM_ANON)
uninit_new(p, pg_round_down(upage), init, type, aux, anon_initializer);
else if (type == VM_FILE)
uninit_new(p, pg_round_down(upage), init, type, aux, file_backed_initializer);
p->writable = writable;
/* TODO: Insert the page into the spt. */
success = spt_insert_page(spt, p);
}
return success;
err:
return success;
}
void
uninit_new (struct page *page, void *va, vm_initializer *init,
enum vm_type type, void *aux,
bool (*initializer)(struct page *, enum vm_type, void *)) {
ASSERT (page != NULL);
*page = (struct page) {
.operations = &uninit_ops,
.va = va,
.frame = NULL, /* no frame for now */
.uninit = (struct uninit_page) {
.init = init,
.type = type,
.aux = aux,
.page_initializer = initializer,
}
};
}
swap_in()์ ํตํด ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ ์ด๋ค์์ผ๋ก ๋ฐ์ดํฐ๊ฐ ์ ์ฌ ๋๋์ง๋ฅผ ์๊ธฐ์ ์ PintOS์์์ ํ์ด์ง ํ ์ด๋ธ์ธ pml4์ vm_get_frame()์ ํตํด ํ๋ ์๊ณผ 4KB์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋น๋ฐ๋ ๊ณผ์ ์ ๋จผ์ ์์๋ณด์๋ค.
/* Obtains a single free page and returns its kernel virtual
address.
If PAL_USER is set, the page is obtained from the user pool,
otherwise from the kernel pool. If PAL_ZERO is set in FLAGS,
then the page is filled with zeros. If no pages are
available, returns a null pointer, unless PAL_ASSERT is set in
FLAGS, in which case the kernel panics. */
void *
palloc_get_page (enum palloc_flags flags) {
return palloc_get_multiple (flags, 1);
}
- palloc_get_page() ํจ์์ ์ฃผ์์ "Obtains a single free page and returns its kernel virtual address"๋ผ๊ณ ์ ํ์๋ค.
์๋์์ ๋ค๋ฃฐ ๋ด์ฉ์ด์ง๋ง ์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ ์ฆ frame์ ํ ๋น๋ฐ๊ธฐ ์ํด์๋ palloc_get_page() ํจ์๋ฅผ ํธ์ถ ํด์ผํ๋ค. ํ์ง๋ง ํด๋น ํจ์ ์ฃผ์์ ๋ณด๋ฉด ๋ฆฌํด๊ฐ์ด kernel virtual address๋ผ๊ณ ์ ํ์๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ ์ด๋ ์๋ ๋ด์ฉ์ ํตํด ์ ๊ทธ๋ฐ์ง ์ ์ ์์๋ค. ๋ชจ๋ ๋ด์ฉ์ ์ ํํ ์ ๋๋ก ์์๋ด์ง๋ ๋ชปํ์ง๋ง ๋๋ฆ๋๋ก ํผ์ ์ดํดํ ๋ด์ฉ์ ์ ์๋ค.
์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ 0๋ฒ ์ฃผ์๋ถํฐ mem_end ์ฆ ๋ฉ๋ชจ๋ฆฌ์ ๋ ์ฃผ์๊น์ง va์ ๋งตํํ์ฌ ํ์ด์ง ํ ์ด๋ธ์ ๊ตฌ์ฑํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋ ์ปค๋ ์์ญ๊ณผ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๊ฐ 1:1 ๋งตํ ๋๋ค๋ ๋ป์ด๋ค. ์ค์ PintOS ๊ณผ์ ์ค ์ง์ ์๋ต์ ํตํด ์๊ฒ๋ ๋ด์ฉ๊ณผ ์ผ์นํ๋ค.
์ปค๋ ์์ญ์ va์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ pa๊ฐ 1:1 ๋งตํ๋์ด ์๋ค๊ณ ํ์๋ ์ ํจ์์ ์ฃผ์์ฒ๋ผ palloc_get_page() ํจ์๋ฅผ ํตํด ๋น์ด ์๋ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ ์ฃผ์์ ํด๋นํ๋ kva(kernel virtual address)๋ฅผ returnํ๋ฉด ๊ฒฐ๊ตญ์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ 4KB ๊ณต๊ฐ์ ํ ๋นํด์ค๊ฑฐ๋ ๋ง์ฐฌ๊ฐ์ง์ผ ๊ฒ์ด๋ค.
palloc_get_page() ํจ์๋ฅผ ํธ์ถํ๋ฉด ์ ํจ์๊ฐ ํธ์ถ๋๋๋ฐ ์ ํจ์๋ฅผ ํตํด ์ปค๋ ์์ญ์ ๋น ๊ณต๊ฐ์ ๋นํธ๋งต์ผ๋ก ๊ด๋ฆฌ๋๊ณ ์๋ ๊ฒ์ ์ ์ ์์๋ค. bitmap_scan_and_flip() ํจ์๋ฅผ ํตํด ๋นํธ๋งต์ผ๋ก ๊ด๋ฆฌ๋๋ kva์ ๋น ๊ณต๊ฐ์ ์ฐพ์ ํด๋น index๋ฅผ ๋ฐํํด์ฃผ๊ณ ํด๋น ๊ณต๊ฐ์ด ํ ๋น๋์๋ค๊ณ true๋ก flip ํด์ค๋ค.
์ ๋ด์ฉ์ ์ดํดํ๊ณ ์๋ ์ฝ๋๋ฅผ ๋ณด๋ฉด frame์ ํ ๋นํด์ฃผ๋ vm_get_frame() ํจ์ ๊ณผ์ ์ ์ฝ๊ฒ ์ดํดํ ์ ์์ ๊ฒ์ด๋ค.
/* The representation of "frame" */
struct frame {
void *kva;
struct page *page;
struct list_elem frame_elem;
};
frame์ ๊ตฌ์ฑํ๊ณ ์๋ ๋ฉค๋ฒ๋ค ์ค kva๋ kernel virtual address๋ฅผ ๋ํ๋ด๋ฉฐ page๋ ํด๋น ํ๋ ์๊ณผ ๋งตํ ๋์ด์ง๋ ํ์ด์ง์ ์ ๋ณด๋ฅผ, frame_elem์ ํ๋ ์๋ค์ ๋ฆฌ์คํธ๋ก ์ฐ๊ฒฐ์์ผ์ฃผ๊ธฐ ์ํ ๊ตฌ์ฑ์์์ด๋ค.
PintOS์์ frame ๊ตฌ์กฐ์ฒด๋ ์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์์์ 4KB ๊ณต๊ฐ์ด ํ ๋น๋ ํ๋ ์์ ๋ํ๋ด๋ ๊ฒ์ด ์๋๋ผ ํด๋น ํ๋ ์์ ๋ถ๊ฐ์ ์ธ ์ ๋ณด๋ค์ ๋ด๊ธฐ์ํ ๊ตฌ์กฐ์ฒด์ด๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ frame ๊ตฌ์กฐ์ฒด๋ malloc์ผ๋ก ํ ๋นํด์ฃผ์ด์ผ ํ๋ฉฐ ์๋ ์ฝ๋์์๋ ๋ณผ ์ ์๋ฏ์ด frame->kva๋ palloc_get_page(PAL_USER) ๋ฅผ ํตํด ์ค์ ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ ์ค 4KB๋ฅผ ํ ๋น ๋ฐ์์ผ ํ๋ค.
๋ค์ vm_do_claim_page() ํจ์๋ก ๋์์ ๋ ธ๋์ ๋ฐ์ค์ ๋ด์ฉ์ ์ดํด๋ณด๋ฉด ์์์ vm_get_frame()์ ํตํด ํ ๋น ๋ฐ์ frame๊ณผ page๋ค์ ๋งตํ ์์ผ์ฃผ๊ณ , ํด๋น ๋ด์ฉ์ pml4 ํ ์ด๋ธ์ ๋ฃ์ด์ฃผ๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
pml4_set_page() ํจ์๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋ ๊ณผ์ ์ ๊ทธ๋ฆผ์ผ๋ก ๊ทธ๋ ค๋ณด์๋ค.
pml4์๋ ์์ ๋ณด์๋ kva์ pa์ ๋งตํ์ด ์ด๋ฏธ ๋์ด ์๋ ์ํ์ด๋ฉฐ ์ดํ pml4_set_page() ํจ์๋ฅผ ํตํด ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋ va์ ํด๋น kva๋ฅผ ๋งตํ ์์ผ์ฃผ๋ฉด va๋ฅผ ํตํด ๋ฌผ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ ๊ฒ์ด๋ค.
์ฌ๊ธฐ๊น์ง๊ฐ PintOS์์์ pml4, frame ํ ๋น ๊ณผ์ ์ ์ดํดํ ๋ด์ฉ์ด๋ฉฐ ๋ค์ swap_in ๊ณผ์ ์ผ๋ก ๋์์์ ์ดํด๋ณด๋ฉด
uninit_new() ํจ์๋ฅผ ํตํด ์ค์ ํด๋์๋ uninit_ops์ swap_in ํจ์์ธ uninit_initialize๊ฐ ํธ์ถ๋๋ค.
์ด๋ return์ด ์ ํ ๋ผ์ธ์์ ํ์ ์ ๋ฐ๋ผ ํด๋นํ๋ initializer๊ฐ ํธ์ถ๋๋ฉฐ, ์์ ๋ฃ์ด๋ initํจ์์ธ lazy_load_segment ํจ์๋ ํจ๊ป ํธ์ถ๋๋ค.