운영체제(구현) - 핀토스 - Virtual memory - Memory Mangement

연도·2024년 6월 12일
0

운영체제 이론&구현

목록 보기
14/19

프로젝트 2와 비교

프로젝트 2

  • page를 가상 메모리에 올릴 때 install_page 함수를 통해 가상 메모리에 올리는 동시에 해당 페이지를 물리 메모리 상에도 올려줌.

프로젝트 3

  • 최초의 setup_stack만 frame 상에 바로 올려주고 그 후에 늘어나는(stack growth) 스택에 대해서는 lazy_load 방식을 사용해서 물리 메모리에 올려준다.

들어가기 전 브리핑

  • 현재 상태에서 Pintos는 가상 및 물리적 메모리 매핑을 관리하기 위한 pml4을 가지고 있다.
  • pml4 이외에 추가로, page fault 및 리소스 관리를 처리하기 위해 spt가 필요하다.

1. page 구조체 bucket_elem 추가, spt 초기화, spt에서 va에 해당하는 page를 찾기, spt에 struct page 삽입.

step1 - 1 (vm.h) struct page

  • page 구조체에 bucket_elem 추가.
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

  • supplemental_page_table에 hash 구조체추가.
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; // 존재하지 않을 경우에만 삽입
}

2. FRAME MANAGEMENT

들어가기 전 브리핑

지금 모든 페이지는 페이지가 만들어졌을 때의 메모리에 대한 메타 정보만을 가지고 있지 않다.

따라서 우리는 물리 메모리(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);
}

0개의 댓글