[SW정글 74일차] pintos p3 part1. memory management

rg.log·2022년 12월 1일
1

SW 사관학교 JUNGLE

목록 보기
18/31

간단하게 깃북과 함께 보며 코드를 분석 후 구현해보기로 했다.

page

"부모 클래스"의 일종으로, uninit_page, file_page, anon_page, 페이지 캐시(프로젝트 4)의 네 개의 "자녀 클래스"를 가지고 있다.

struct page
{
	const struct page_operations *operations;
	void *va;			 /* user 공간 주소 */
	struct frame *frame; /* frame 역참조 */

	/* Your implementation */

	/* 유형별 데이터가 유니언에 바인딩. 각 함수는 현재 유니언을 자동으로 감지 */
	union
	{
		struct uninit_page uninit;
		struct anon_page anon;
		struct file_page file;
#ifdef EFILESYS
		struct page_cache page_cache;
#endif
	};
};

💡 union
union내의 멤버 중 가장 큰 데이터형의 메모리를 할당받고, 메모리공간을 공유하기에 멤버변수를 한번에 하나씩만 사용 가능하다.

frame

struct frame
{
	void *kva;	/* 커널의 가상 주소 */
	struct page *page;	/* page 역참조 */
};

page_operations

페이지 작업에 대한 함수 포인터를 가진 함수 테이블로, 구조체의 멤버에 method를 넣어두고 필요시마다 호출한다.

struct page_operations
{
	bool (*swap_in)(struct page *, void *);
	bool (*swap_out)(struct page *);
	void (*destroy)(struct page *);
	enum vm_type type;
};

해당 struct가 사용되는 과정을 보기 위해서 예시를 들어보자.
vm_dealloc_page 함수는 할당된 페이지를 free 시켜주는 함수다.

void vm_dealloc_page(struct page *page)
{
	destroy(page);
	free(page);
}

destory는 아래와 같이 정의되어있다.

#define destroy(page) if ((page)->operations->destroy) (page)->operations->destroy (page)

destory에 인자로 들어온 page의 operations의 destory를 실행시키겠다는 것이다.

page는 위에서 말했듯 project3까지는 page가 uninit_page, file_page, anon_page 세 가지 유형 중 한 가지 유형이 될 수 있으니 file_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의 유형은 VM_FILE이기에 .destory는 file_backed_destory를 가리키고 있는 것이고, file-backed page 의 destroy가 실행된다.

static void
file_backed_destroy (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;
    // 아직 미구현이다.
}

Supplemental Page Table

우리의 pintos는 가상 메모리와 물리 메모리의 매핑을 관리할 pml4라는 테이블을 갖고 있으나 충분하지 않다. page fault 및 자원 관리를 위해선 추가 정보를 저장할 보충 페이지 테이블이 필요하고 한다. 뭐.. 그럼 만들어야지. 하며 당차게 시작했으나 처음부터 막힌건 어떤 데이터 스트럭쳐로 할지 골라야한다. 우리에게 자유롭게 디자인하라고 한다. 해야할 많은 과제 가운데 자유를 주니까 꽤 불편했다. 모든 경우의 수를 생각해야만 하는 것 같아서..^^

우리 조는 해시를 골랐다. 비트맵은 낯설고, 리스트와 배열은 효율이 덜 좋아보였다. 자, 해시 관련 깃북 페이지가 따로 있다.🫣

supplemental_page_table_init

spt를 초기화하는 함수로, 새 프로세스 시작과 프로세스 fork시 호출된다. 프로세스 (우리의 작은 핀토스에서는 1프로세스 1쓰레드이기에 쓰레드) 별로 각자의 spt를 갖는다.

void supplemental_page_table_init(struct supplemental_page_table *spt UNUSED)
{
	hash_init(&spt->spt_hash, spt_hash_func, spt_less_func, NULL);
}

struct page에 아래 멤버를 추가한다.

struct hash_elem hash_elem; /* 해시 테이블 elem */

struct page의 va를 키로 해시함수와 비교함수를 작성한다.

static unsigned spt_hash_func(const struct hash_elem *e, void *aux UNUSED)
{
	const struct page *p = hash_entry(e, struct page, h_elem);
	return hash_bytes(&p->va, sizeof p->va);
}

static bool spt_less_func(const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED)
{
	const struct page *ap = hash_entry(a, struct page, h_elem);
	const struct page *bp = hash_entry(b, struct page, h_elem);

	return ap->va < bp->va;
}

💡UNUSED
우리의 핀토스 스켈레톤 코드에는 UNUSED가 많이 사용되어 있다. 매개 변수를 사용하지 않으면 컴파일러 경고가 표시되지만 사용하면 해당 매개 변수가 사용되지 않는다는 경고를 억제시킬 수 있다.

spt_find_page

struct page *
spt_find_page(struct supplemental_page_table *spt, void *va)
{
	struct page *page = NULL;
	struct page *temp = (struct page *)malloc(sizeof(struct page));
	temp->va = pg_round_down(va);

	// 가짜 페이지와 같은 hash를 가지는 페이지를 찾아옴
	struct hash_elem *va_hash_elem = hash_find(&spt->spt_hash, &temp->h_elem);

	// 가짜 페이지 메모리 해제
	free(temp);

	if (va_hash_elem != NULL)
	{
		page = hash_entry(va_hash_elem, struct page, h_elem);
	}
	return page;
}

spt에서 va 주소에 해당하는 page 구조체를 찾는 함수다. 실패하면 NULL을 리턴한다.

spt_insert_page

bool 
spt_insert_page(struct supplemental_page_table *spt,
					 struct page *page){
	int succ = false;
	if (hash_insert(&spt->spt_hash, &page->h_elem) == NULL){
		succ = true;
	}
	return succ;
}

유효성 검사와 함께 페이지를 spt에 넣는다.


Frame 관리

vm_get_frame

static struct frame *
vm_get_frame(void){
	struct frame *frame = NULL;
	void *new_kva = palloc_get_page(PAL_USER); // user pool에서 새 물리 페이지를 가져옴
    
	if (new_kva == NULL){ // 페이지 할당 실패시
		PANIC("todo");
	}else{ // 페이지 할당 성공시, 프레임 초기화
		frame = (struct frame *)malloc(sizeof(struct frame)); 
		frame->kva = new_kva;
		frame->page = NULL;
	}
	frame->thread = thread_current();

	ASSERT(frame != NULL);
	ASSERT(frame->page == NULL);
	return frame;
}

palloc_get_page를 호출하여 user pool에서 새 물리 페이지를 가져오는 함수다. user pool에서 메모리를 가져와서 해당 멤버들을 초기화하고 리턴한다. 이 함수 구현 후에는 모든 user 공간 페이지들은 이 함수를 이용해서 할당해야한다.

페이지 할당 실패해도 지금은 swap out하지 않고, PANIC ("todo")으로 처리하면 된다.

vm_do_claim_page

bool 
vm_do_claim_page(struct page *page){
	struct frame *frame = vm_get_frame();
	int result = false;
	struct thread *t = thread_current();

	/* Set links */
	frame->page = page;
	page->frame = frame;

	/* TODO: Insert page table entry to map page's VA to frame's PA. */
	if (!install_page(page->va, frame->kva, page->writable))
		return false;
        
	return swap_in(page, frame->kva);
}

claim은 물리 프레임을 할당한다는 뜻으로, vm_get_frame를 호출하여 프레임(할당된 페이지)을 얻은 후 mmu를 설정한다. 이는 페이지 테이블(pml4)안에 가상 주소에서 물리 주소로의 매핑하는 것이다.

vm_claim_page

bool vm_claim_page(void *va UNUSED){
	struct page *page = NULL;

	page = spt_find_page(&thread_current()->spt, va);

	if (page == NULL){
		return false;
	}
	return vm_do_claim_page(page);
}

va에 할당된 페이지를 할당하는 함수다.
현재 thread 구조체에 있는 spt에서 va에 매핑된 page를 찾아 vm_do_claim_page을 호출하여 물리 프레임을 할당한다.


오늘의 나는

협력사 네이버 이사님과 정글 수료생분들이 왔다 가셨다. 주시는 말씀에서 네이버로써의 자부심과 함께 중압감이 느껴졌다. 다른 회사에선 할 수 있지만 네이버와 같이 전국민이 알고 큰 기업에서는 할 수 없는 것들과 그를 책임지는 입장에서 내 뒤를 맡길 누군가를 뽑는 일은 아주 중요하다고 하셨다. 보통 지원자의 입장에서 생각하던 많은 자격 요건들과 후기들이 와닿는 시간이었다.

스스로에 대한 자부심과 함께 겸손을. 몰입과 함께 기록을. 단점을 위해 에너지를 쏟기보단 장점을 살리길. 솔직함과 더불어 신뢰갈만한 에티튜드를.

0개의 댓글