가상메모리 페이지 - page
물리 메모리 페이지 -frame
supplemental page table -> physical memory page frame
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
};
};
struct page_operations {
bool (*swap_in)(struct page *, void *);
bool (*swap_out)(struct page *);
void (*destroy)(struct page *);
enum vm_type type;
};
이 구조체는 3개의 함수 포인터를 포함한 하나의 함수 테이블로 이해하면 됨
한 페이지는 다음 중 하나의 타입을 가짐:
각 페이지 유형에 따라 다른 함수 포인터 테이블을 가짐 (예: anon_ops, file_ops)
예: destroy(page) 호출 시 → page->operations->destroy(page) 실행됨
page->operations = &anon_ops; // 익명 페이지면
destroy(page); // 라고만 써도
→ page->operations->destroy(page);
→ anon_destroy(page); // 타입에 맞는 함수가 호출됨
destroy 매크로 정의:
#define destroy(page) \
if ((page)->operations->destroy) (page)->operations->destroy(page)
→ 실제로는 page->operations->destroy(page) 호출과 같음
page가 VM_FILE이므로
→ page->operations는 file_ops를 가리킴
→ file_ops.destroy는 file_backed_destroy를 가리킴
→ 결과적으로 file_backed_destroy(page) 함수가 호출됨
파일 기반 페이지(VM_FILE)는 destroy(page) 호출 시 자동으로 file_backed_destroy()가 실행되도록
함수 포인터 테이블(file_ops)을 통해 연결되어 있다.
기본 페이지 테이블(PML4)은 하드웨어가 사용하는 테이블로, 가상 주소와 물리 주소의 매핑을 관리하지만
페이지 폴트 처리나 페이지 타입 정보 관리 등은 못 하므로 커널이 직접 관리하는 보조 페이지 테이블(SPT)이 필요하다.
보조 페이지 테이블은 각 가상 주소(va)에 대응되는 struct page 정보를 저장하고,
이 페이지가 어떤 타입인지(anon, file, uninit), 어떤 파일에 연결되어 있는지, 스왑 영역에 있는지 등을 추적한다.
void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
hash_init(&spt->spt_hash, page_hash, page_less, NULL);
}
/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
struct page *page = NULL;
uint64_t key=hash_bytes(&va,sizeof(va));
struct hash_iterator i;
hash_first (&i, &spt -> spt_hash);
while (hash_next (&i))
{
struct page *p = hash_entry (hash_cur (&i), struct page, hash_elem);
if (p->va==va){
page=p;
break;
}
}
return page;
}
/* Insert PAGE into spt with validation. */
bool
spt_insert_page (struct supplemental_page_table *spt UNUSED,
struct page *page UNUSED) {
bool succ = false;
/* TODO: Fill this function. */
// uint64_t key=hash_bytes(&page->va,sizeof(page->va));
if (spt_find_page(spt, page->va)==NULL){
hash_insert(&spt->spt_hash, &page->hash_elem );
succ=true;
};
return succ;
}
보조 테이블은 가상 주소에 대한 정보를 담고 있지만,
실제로 데이터를 담는 물리 메모리 페이지는 frame이 관리한다.
struct frame {
void *kva; // 커널 가상 주소 (실제로 데이터를 담는 곳)
struct page *page; // 이 프레임에 연결된 가상 페이지
};
static struct frame *
vm_get_frame (void) {
struct frame *frame = NULL;
/* TODO: Fill this function. */
struct frame *frame=palloc_get_page(PAL_USER);
frame->kva=NULL;
frame -> page=NULL;
ASSERT (frame != NULL);
ASSERT (frame->page == NULL);
return frame;
}
static bool
vm_do_claim_page (struct page *page) {
struct frame *frame = vm_get_frame ();
/* Set links */
frame->page = page;
page->frame = frame;
/* TODO: Insert page table entry to map page's VA to frame's PA. */
//pml4에서 해당 pte를 갖고와라?
if (!spt_insert_page(&thread_current()->spt, page)){
return false;
}
return swap_in (page, frame->kva);
}
bool
vm_claim_page (void *va UNUSED) {
struct page *page = NULL;
/* TODO: Fill this function */
if (va==NULL) return false;
if (page=spt_find_page(&thread_current()->spt,va)==NULL){
page=palloc_get_page(PAL_USER);
page->va=va;
}
return vm_do_claim_page (page);
}
[ 가상 주소 va ]
↓
spt_find_page(spt, va) // 보조 테이블에서 페이지 존재 확인
↓ (없으면)
spt_insert_page(spt, page) // 새 페이지 생성 후 테이블에 삽입
↓
vm_claim_page(va) // 이 주소에 페이지를 실제로 매핑하기 위해
↓
vm_do_claim_page(page) // 페이지에 프레임 연결
↓
vm_get_frame() // 새 물리 메모리 프레임 할당
↓
page <-> frame 연결
↓
MMU(PML4) 에 매핑 추가 // 가상 주소와 물리 주소 연결 완료
/* Copy supplemental page table from src to dst */
//fork 시 부모 → 자식 SPT 복사
bool
supplemental_page_table_copy (struct supplemental_page_table *dst UNUSED,
struct supplemental_page_table *src UNUSED) {
memcpy(&dst->spt_hash, &src->spt_hash, sizeof(struct supplemental_page_table));
}
/* Free the resource hold by the supplemental page table */
void
supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED) {
/* TODO: Destroy all the supplemental_page_table hold by thread and
* TODO: writeback all the modified contents to the storage. */
hash_destroy(&spt->spt_hash, &spt->spt_hash.hash);
}
unsigned
page_hash (const struct hash_elem *p_, void *aux UNUSED) {
const struct page *p = hash_entry (p_, struct page, hash_elem);
return hash_bytes (&p->va, sizeof p->va);
}
/* Returns true if page a precedes page b. */
bool
page_less (const struct hash_elem *a_,
const struct hash_elem *b_, void *aux UNUSED) {
const struct page *a = hash_entry (a_, struct page, hash_elem);
const struct page *b = hash_entry (b_, struct page, hash_elem);
return a->va < b->va;
}
struct page *
page_lookup (const void *address) {
struct page p;
struct hash_elem *e;
p.va = address;
e = hash_find (&pages, &p.hash_elem);
return e != NULL ? hash_entry (e, struct page, hash_elem) : NULL;
}
profile
쉽네 ㅋ