if (!vm_alloc_page(type, upage, writable)) // uninit page 생성 & 초기화
return false;
// vm_claim_page으로 요청해서 매핑 & 페이지 타입에 맞게 초기화
if (!vm_claim_page(upage))
return false;
// 매핑된 프레임에 내용 로딩
struct page *dst_page = spt_find_page(dst, upage);
memcpy(dst_page->frame->kva, src_page->frame->kva, PGSIZE);
우선 supplemental_page_table_copy 함수 입니다.
init이랑 aux는 Lazy Loading에 필요하지만 지금 만드는 페이지는 기다리지 않고 바로 내용을 넣어줄 것이므로 init과 aux는 필요가 없습니다.
때문에 file과 다르게 aux를 만들고 값을 넣는 과정은 생략됩니다.
또한 initializer 도 생략됩니다.
/* anon_intializer 는 익명 페이지 초기화 함수이다.
페이지에 대한 초기 설정 및 처리를 수행. */
bool
anon_initializer (struct page *page, enum vm_type type, void *kva) {
/* Set up the handler */
struct uninit_page *uninit = &page->uninit;
//uninit 페이지 구조체를 0으로 초기화.
memset(uninit,0, sizeof(struct uninit_page));
//페이지의 operation을 anon 페이지 연산으로 설정.
page->operations = &anon_ops;
//anon 페이지 구조체를 페이지의 anon 포인터에 할당.
struct anon_page *anon_page = &page->anon;
return true ;
}
다음은 anon_intializer 입니다.
anon_intializer 는 익명 페이지 초기화 함수입니다.
// 코드를 보시면 페이지에 대한 초기 설정 및 처리를 수행합니다.
// 그 다음 uninit 페이지 구조체를 0으로 초기화합니다.
// 페이지의 operation을 anon 페이지 연산으로 설정한 후
// anon 페이지 구조체를 페이지의 anon 포인터에 할당합니다.
/* 스왑 디스크로부터 페이지 내용을 읽어와 페이지를 스왑인하여 현재 메모리에 복구하는 함수. */
static bool
anon_swap_in (struct page *page, void *kva) {
struct anon_page *anon_page = &page->anon;
//페이지의 스왑 아웃된 sector를 가져옴.
disk_sector_t sector = page->anon.sector;
//8개의 디스크 섹터에서 페이지 데이터를 읽는다.
for (int i = 0; i < 8; i++) {
disk_read(swap_disk,sector+i,page->frame->kva + i * 512);
}
//스왑 비트맵에서 해당 섹터를 사용 가능한 것으로 표시.
bitmap_set_multiple(bitmap,sector,8,false);
//페이지의 스왑 섹터를 0으로 설정하여 스왑 아웃되지 않았음을 표시.
page->anon.sector = 0;
//페이지를 가상 주소와 물리 주소를 매핑한다.
pml4_set_page(thread_current()->pml4,page->va,kva,page->writable);
return true;
}
다음은 anon_swap_in 입니다.
스왑 디스크로부터 페이지 내용을 읽어와 페이지를 스왑인하여 현재 메모리에 복구하는 함수입니다.
// 우선 페이지의 스왑 아웃된 sector를 가져와서
8개의 디스크 섹터에서 페이지 데이터를 읽어옵니다.
// 그 후 스왑 비트맵에서 해당 섹터를 사용 가능한 것으로 표시합니다.
페이지의 스왑 섹터를 0으로 설정하여 스왑 아웃되지 않았음을 표시한 후
페이지를 가상 주소와 물리 주소를 매핑한다.
/* 페이지의 내용을 스왑 디스크로 스왑아웃하는 함수. */
static bool
anon_swap_out (struct page *page) {
struct anon_page *anon_page = &page->anon;
//사용 가능한 섹터를 비트맵에서 찾고, 8개의 섹터를 사용 중으로 표시.
disk_sector_t sector = bitmap_scan_and_flip(bitmap,0,8,false);
//페이지의 스왑 섹터 정보를 업데이트.
page->anon.sector = sector;
//반복문을 사용하여 페이지의 데이터를 8개의 디스크 섹터에 씁니다.
for (int i=0; i < 8; i++) {
disk_write(swap_disk,sector+i,page->frame->kva + i * 512);
}
//맵핑을 끊는다.
pml4_clear_page(thread_current()->pml4,page->va);
return true;
}
다음은 anon_swap_out 입니다.
이 함수는 페이지의 내용을 스왑 디스크로 스왑아웃하는 함수입니다.
// 우선 사용 가능한 섹터를 비트맵에서 찾고, 8개의 섹터를 사용 중으로 표시합니다.
그리고 페이지의 스왑 섹터 정보를 업데이트합니다.
그 후 반복문을 사용하여 페이지의 데이터를 8개의 디스크 섹터에 쓴 다음
맵핑을 끊습니다.
if (type == VM_FILE)
{
struct lazy_aux *file_aux = malloc(sizeof(struct lazy_aux));
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 정보를 복사하기위해서 lazy_aux 구조체를 새로 할당받아 정보를 복사해준다.
vm_alloc_page_with_initializer함수를 통해서 page를 할당받고
이렇게 만든 페이지가 supplement page table에 있는지 확인해준다.
vm_type이 file_backed라는걸 알고있으므로 초기화를 진행해준다.
복사하고자 하는 page와 똑같은 프레임을 매핑해준다.
static const struct page_operations file_ops = {
.swap_in = file_backed_swap_in,
.swap_out = file_backed_swap_out,
.destroy = file_backed_destroy,
.type = VM_FILE,
};
{
page->operations = &file_ops;
struct file_page *file_page = &page->file;
struct lazy_aux *lazy_load_arg = (struct lazy_aux *)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;
return true;
}
Page_operations의 swap_in, swap_out, destroy를 vm_type에 맞게 초기화해준다.
그리고 page->file를 page의 uninit.aux에 넣어준다.
이렇게 초기화하여 페이지가 파일 데이터를 읽거나 쓰는걸 준비시켜준다.
// 스왑인 할때 쓰는 함수
{
struct file_page *file_page UNUSED = &page->file;
file_seek(file_page->file, file_page->ofs);
file_read(file_page->file, kva, file_page->read_bytes);
pml4_set_page(thread_current()->pml4, page->va, kva, page->writable);
return true;
}
file_backed를 swap_in해주기 위한 함수이다.
file의 pos를 ofs으로 바꾸고
file을 kva(buffer)에 read_byte만큼 읽어와 저장한다.
그 후 물리 메모리 매핑해준다.
#file_backed_swap_out (struct page *page){
struct file_page *file_page = &page->file;
if(pml4_is_dirty(thread_current()->pml4, page->va)) {
file_seek(file_page->file, file_page->ofs);
file_write(file_page->file, page->frame->kva, file_page->read_bytes);
pml4_set_dirty(thread_current()->pml4, page->va,false);
}
pml4_clear_page(thread_current()->pml4, page->va);
page->frame = NULL;
return true;
}
file_backed를 swap_out해주기 위한 함수이다.
DIRTY인 경우 1일 경우,
file seek를 통해 file을 file_page에 저장된 ofs(file의 처음)에서부터 읽을 수 있도록 합니다.
kva버퍼에서 file로 read_bytes만큼 쓴다.
dirty비트를 0으로 설정하여 파일이 수정되었다고 알려준다.
변경사항이 없다면 바로 물리 메모리 매핑을 끊어주고 아니면 앞을 순서대로 진행한 후 매핑을 끊어준다.