[PintOS] Project 3 : VM - Stack Growth - 구현 함수

CorinBeom·2025년 6월 5일

PintOS

목록 보기
13/19
post-thumbnail

이번 포스팅에서는 어떤 함수에 어떤 로직을 구현해야 하는지 설명하도록 하겠다.


1. vm_try_handle_fault(struct intr_frame *f, void *addr, ...)

  • 역할 : Page Fault 발생 시 처리 진입점

  • 구현 포인트 :

    • falut가 rsp - 8 근처에서 발생했고, USER_STACK 범위 이내면 → vm_stack_growth()호출
    • 이후 spt_find_page(), vm_do_claim_page() 통해 매핑까지 완료
bool vm_try_handle_fault(struct intr_frame *f, void *addr, bool user, bool write, bool not_present)
{
	struct supplemental_page_table *spt = &thread_current()->spt;
	struct page *page = NULL;
	// 현재 커널/유저 모드에 따라 올바른 RSP를 선택
	uint64_t user_rsp = user ? f->rsp : thread_current()->user_rsp;

	if (not_present)
	{
		// [1] Stack growth: 접근 주소가 RSP 아래거나, 전체 스택 범위 내이면 자동 확장 허용
		if (user_rsp - 8 == addr ||
			(USER_STACK - (1 << 20) <= user_rsp && user_rsp < addr && addr < USER_STACK))
		{
			vm_stack_growth(addr);
			return true;
		}

		// [2] Lazy loading: SPT에서 해당 주소에 등록된 페이지가 있는지 확인
		page = spt_find_page(spt, pg_round_down(addr));
		
		// (1) 없는 주소이거나, (2) 쓰기 요청인데 read-only 페이지일 경우 → 강제 종료
		if (page == NULL || (write && !page->writable))
			exit(-1);
	}
	else if (write)
	{
		// Protection fault: 존재하는 페이지지만 쓰기 허용되지 않은 경우 → 종료
		exit(-1);
	}

	// 정상적인 페이지 접근 → 물리 메모리에 매핑 시도 (lazy load 수행)
	return vm_do_claim_page(page);
}

2. vm_stack_growth(void *addr)

  • 역할 : 스택 확장을 위한 페이지 생성 + claim

  • 구현 포인트 :

    • pg_round_down(addr) 를 기준으로 anonymous 페이지를 lazy 방식으로 등록
    • 등록 후 즉시 vm_claim_page()로 물리 메모리 확보
/* 유저 스택 확장용 함수
 * - 접근한 주소를 기준으로 anonymous 페이지 할당 및 claim */
static void vm_stack_growth(void *addr UNUSED)
{
	vm_alloc_page_with_initializer(VM_ANON, pg_round_down(addr), 1, NULL, NULL);
	vm_claim_page(pg_round_down(addr));
}

3. vm_claim_page(void *va)

  • 역할 : 실제로 페이지를 매핑하고 물리 메모리 확보

  • Stack Growth에서 : vm_stack_growth() 내에서 사용됨

bool
vm_claim_page (void *va UNUSED) {
	struct page *page = NULL;
	/* TODO: 이 함수를 구현하세요. */
	page = spt_find_page(&thread_current()->spt, va);
	if (page == NULL)
		return false;
	return vm_do_claim_page (page);
}

4. vm_do_claim_page(struct page *page)

  • 역할 : 프레임 할당 → pml4에 매핑 → swap_in

  • Stack Growth에서 : claim 과정의 내부 동작

vm_do_claim_page (struct page *page) {
	struct frame *frame = vm_get_frame ();

	/* 연결 설정 */
	frame->page = page;
	page->frame = frame;

	/* TODO: 페이지의 VA를 프레임의 PA에 매핑하기 위한 페이지 테이블 엔트리를 삽입하세요. */
	struct thread *current = thread_current();

	pml4_set_page(current->pml4, page->va, frame->kva, page->writable);
	
	return swap_in(page, frame->kva);
}

정리: Stack Growth 관련 함수 트리 흐름

Page Fault
 └─> vm_try_handle_fault()
       ├─> 스택 접근 조건 검사 → vm_stack_growth()
       │     ├─> vm_alloc_page_with_initializer()
       │     │     └─> anon_initializer 설정 + SPT 등록
       │     └─> vm_claim_page()
       │           └─> vm_do_claim_page()
       │                 ├─> vm_get_frame()
       │                 ├─> pml4_set_page()
       │                 └─> swap_in()
       └─> spt_find_page() → 매핑 여부 확인

Stack Growth는 구현할 함수가 사실 많지는 않았던 것 같다.
정리하면서 함수들을 작성했지만 정작 수정한 함수는 5개 이내였던 것 같다.
다만 구현을 하고 테스트를 돌리면서 원래 잘 작동되던 테스트 케이스들이 문제가 생기는 일이 많았다.

Stack Growth의 함수들을 완성하면

...
pass tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
pass tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
pass tests/vm/pt-write-code
pass tests/vm/pt-write-code2
pass tests/vm/pt-grow-stk-sc
...

이러한 결과가 나온다.
사실 이거보다 더 많이 통과되는 것 같은데 기존 로직의 문제인지 구현 코드의 문제인지
다른 블로그들을 찾아봤을 때, 그 블로그들의 결과보다 테스트가 적게 통과했다.

Project 3에 들어오니 디버깅 지옥이라서 정신적으로나 육체적으로나 많이 지치고 힘들어지는 것 같다.

그래도 마지막까지 화이팅 해보자 !

profile
Before Sunrise

0개의 댓글