프로젝트 2
프로젝트 3
들어가기 전 브리핑
step1 - 1 (vm.h) struct page
struct page {
const struct page_operations *operations; /* 페이지 연산에 대한 포인터 */
void *va; /* 가상 주소 */
struct frame *frame; /* Back reference for frame */
bool writable; /* 페이지가 쓰기 가능한지 여부 */
/* Your implementation */
struct hash_elem bucket_elem; /* 해시 테이블 요소*/
/* Per-type data are binded into the union.
* Each function automatically detects the current union */
// 멤버 보유
union {
struct uninit_page uninit;
struct anon_page anon;
struct file_page file;
#ifdef EFILESYS
struct page_cache page_cache;
#endif
};
};
step1 - 2 (vm.h) struct supplemental_page_table
struct supplemental_page_table {
struct hash spt_hash; // hash 테이블 구조체
};
step 2 - 1 (vm.c) supplemental_page_table_init
💡 spt 초기화코드
void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
hash_init(spt, hash_func, page_less, NULL);
}
spt초기화에 필요한 2개의 함수들
step 2 - 2 (vm.c) hash_func
가상 주소를 해시 값으로 변환해서 해시 테이블에서 빠르게 검색할 수 있도록
코드
unsigned hash_func (const struct hash_elem *e,void *aux)
{
const struct page *p = hash_entry(e, struct page, bucket_elem);
return hash_bytes(&p->va, sizeof p->va);
}
step 2 - 3 (vm.c) page_less
두 가상 주소를 비교해서 해시 테이블에서 키의 순서 결정
코드
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, bucket_elem);
const struct page *b = hash_entry(b_, struct page, bucket_elem);
return a->va < b->va;
}
step 3 (vm.c) spt_find_page
spt에서 va에 해당하는 구조체 페이지를 찾아서 반화
코드
struct page *
spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
struct page *page = NULL;
/* TODO: Fill this function. */
page = malloc(sizeof(struct page));
struct hash_elem *e;
// va에 해당하는 hash_elem 찾기
page->va = va;
e = hash_find(&spt, &page->bucket_elem);
// 있으면 e에 해당하는 페이지 반환
return e != NULL ? hash_entry(e, struct page, bucket_elem) : NULL;
}
step 4 (vm.c) spt_insert_page
page를 spt에 삽입
코드
bool
spt_insert_page (struct supplemental_page_table *spt UNUSED,
struct page *page UNUSED) {
int succ = false;
/* TODO: Fill this function. */
return hash_insert(&spt, &page->bucket_elem) == NULL ? true : false; // 존재하지 않을 경우에만 삽입
}
들어가기 전 브리핑
지금 모든 페이지는 페이지가 만들어졌을 때의 메모리에 대한 메타 정보만을 가지고 있지 않다.
따라서 우리는 물리 메모리(frame)를 관리하는 다른 방식이 필요하다.
프레임 구조체
struct frame {
void *kva; // 커널 가상 주소
struct page *page; // 페이지 구조체를 담기 위한 멤버 변수
};
step 1 (vm.c) vm_get_frame
페이지 할당
코드
static struct frame *
vm_get_frame (void) {
// 1. 프레임 포인터 초기화
struct frame *frame = NULL;
/* TODO: Fill this function. */
// 2. 사용자 풀에서 물리 페이지 할당
void *kva = palloc_get_page(PAL_USER); // 커널 풀 대신에 사용자 풀에서 메모리를 할당하는 이유
// 커널 풀의 페이지가 부족 > 커널 함수들이 메모리 확보 문제 > 큰 문제 발생
// 3. 페이지 할당 **실패 처리**
if(kva == NULL)
{
PANIC("todo"); // 나중에는 swap out 기능을 구현한 후에는 이 부분 수정 예정
}
// 4. 프레임 **구조체** 할당
frame = malloc(sizeof(struct frame)); // 페이지 사이즈만큼 메모리 할당
// 5. 프레임 **멤버** 초기화
frame->kva = kva;
// 6. 유효성 검사
ASSERT (frame != NULL);
ASSERT (frame->page == NULL);
// 7. 프레임 반환
return frame;
}
step 2 (vm.c) vm_do_claim_page
page(va) <> frame(kva) 매핑
코드
static bool
vm_do_claim_page (struct page *page) {
// 1. vm_get_frame을 호출하여 새로운 프레임을 가져오기.
struct frame *frame = vm_get_frame ();
/* Set links */
// 2. 프레임과 페이지 간의 링크 설정
frame->page = page;
page->frame = frame;
/* TODO: Insert page table entry to map page's VA to frame's PA. */
struct thread *current = thread_current();
// 3. 'pml4_set_page'을 호출하여 페이지 테이블에 가상 주소와 물리 주소 간의 매핑 추가.
pml4_set_page(current->pml4, page->va, frame->kva, page->writable);
// 4. 스왑 공간에서 필요한 데이터를 메모리에 로드한다.
return swap_in (page, frame->kva);
}
step 3 (vm.c) vm_claim_page
페이지에 va 할당
코드
bool
vm_claim_page (void *va UNUSED) {
struct page *page = NULL;
/* TODO: Fill this function */
// 1. spt에서 주어진 가상 주소에 **해당**하는 페이지 찾기.
page = spt_find_page(&thread_current()->spt, va);
// 2. 페이지가 없으면 리턴 fail 하고 끝내기
if (page == NULL)
return false;
// 3. 페이지가 존재하면 함수 호출하여 페이지를 할당 and 결과 반환
return vm_do_claim_page (page);
}