[TIL/크래프톤 정글] DAY 92

배재준·2025년 6월 10일

크래프톤 정글 - TIL

목록 보기
84/93
post-thumbnail

2025.06.09

TIL(TODAY I LEARN)


  • 오늘한 내용 : PintOS - Project3: Virtual Memory - stack growth 구현 완료

  • WEEK 13 : 정글 끝까지(PintOS) - Virtual Memory


5. 스택 확장(vm_stack_growth) (vm/vm.c):

스택이 ↓(작은 주소)로 넘어갈 때마다 새로운 익명 페이지를 할당.

  • vm_try_handle_fault() 수정
    • vm_try_handle_fault()kernel context로 진입했을 때도 “실제로 fault_addr가 user 스택 영역이라면” 여전히 유저 스택을 늘려 줌.
    • 이때 user==false 플래그로 단순히 걸러 버리면, 커널이 copy_from_user() 같은 함수로 유저 버퍼를 접근할 때 생긴 스택 폴트를 못 살려 주니까, 우리가 thread->user_rsp_saved를 저장해 두고 “커널 모드라도 user_rsp_saved 기반으로 스택 확장”을 허용했던 것.
    • 요약하자면:
      1. User 모드 fault → 유저 스택 확장 로직
      2. Kernel 모드 fault(유저 메모리 접근 중) → 저장해 둔 user_rsp 기반으로 같은 유저 스택 확장
      3. Kernel 스택 은 전혀 늘어나지 않음.
    • 즉, “커널이든 유저든 각자 스택을 늘린다”가 아니라,
    • “커널 모드에서 발생한 유저 스택 접근 페이지 폴트도 유저 스택을 늘리는 트리거로 삼는다”라고 이해
    • 추가) 여러 페이지를 한번에 늘려야 하는 경우 수정
      • big_stack_obj 테스트
      • while을 통해 여러 페이지마다 확장이 필요

/* Return true on success */
bool vm_try_handle_fault(struct intr_frame *f, void *addr,
						 bool user, bool write, bool not_present)
{
	struct supplemental_page_table *spt UNUSED = &thread_current()->spt;
	struct page *page = NULL;
	/* TODO: Validate the fault */
	/* TODO: Your code goes here */

	/*
		NULL 체크 및 커널 영역 접근은 실패
		is_kernel_vaddr(addr) 는 “어떤 주소를 건드리려 했나” (대상 메모리 영역)
		user 는 “어디서 폴트가 발생했나” (접근 주체의 권한 레벨)

		(수정)user 검사 제외 : 유저 모드, 커널 영역 둘다 스택 확장이 필요할 수 있음
	*/
	if (addr == NULL || is_kernel_vaddr(addr))
	{
		return false;
	}

	/* fault된 가상 주소를 페이지 경계로 반올림 / 주소가 속한 페이지의 첫 부분 */
	void *fault_page = pg_round_down(addr);

	/* RSP 결정: user 모드면 f->rsp, kernel 모드면 저장해 둔 user_rsp_saved */
	void *rsp = user ? f->rsp : thread_current()->rsp_stack;

	/* spt에 예약된(매핑은 안되어 있는 : not_present)
		uninit 페이지가 있으면 물리 메모리로 올리기 */
	if (not_present)
	{
		page = spt_find_page(spt, fault_page);
		if (page == NULL)
		{
			/* 기존 페이지가 없으면 스택 확장 조건 검사 */
			if (addr < USER_STACK											  /* 유저 스택 영역 이내 */
				&& (uintptr_t)USER_STACK - (uintptr_t)fault_page <= (1 << 20) /* 1MB 제한 */
				&& (uintptr_t)addr >= (uintptr_t)rsp - 32 && (uintptr_t)fault_page + PGSIZE >= (uintptr_t)rsp - 32)
			{
				/* stack_bottom 에서 fault_page 까지 한 페이지만큼씩 순차 확장 */
				void *stack_bottom = thread_current()->stack_bottom;
				while (stack_bottom > fault_page)
				{
					void *next_page = stack_bottom - PGSIZE;
					/* 1MB 한도 재검사 (안정성) */
					if ((uintptr_t)USER_STACK - (uintptr_t)next_page > (1 << 20))
						break;
					/* 스택 확장 시도 */
					vm_stack_growth(next_page);
					stack_bottom = next_page;
					/* 확장 후 최하단 경계 갱신 */
					thread_current()->stack_bottom = fault_page;
				}
			}
			/* SPT에 새 페이지가 등록되었으므로 다시 찾기 */
			page = spt_find_page(spt, fault_page);
		}
		/* page가 존재하면 쓰기 권한 검사 후 claim */
		if (page != NULL)
		{
			if (write && !page->writable)
				return false;
			return vm_do_claim_page(page);
		}

		return false;
	}
}
  • 추가) 유저 모드일때의 rsp를 저장 하는 로직을 추가
    • syscall.c (syscall_handler())/ exception.c (page_fault())
exception.c
---------------
static void page_fault(struct intr_frame *f)
{
	...
#ifdef VM
	/* For project 3 and later. */
	/* 유저 모드 폴트일 때만 저장 */
	if (user)
		thread_current()->rsp_stack = f->rsp;

	if (vm_try_handle_fault(f, fault_addr, user, write, not_present))
		return;
#endif
	...
}

-------------------------
syscall.c
-------------------------
void syscall_handler(struct intr_frame *f UNUSED)
{
	...
	/* Project3 VM : stack growth를 위한 추가 */
	thread_current()->rsp_stack = f->rsp;

	int syscall_num = (int)f->R.rax;
	...
}
  • vm_stack_growth() 구현
/* Growing the stack. */
static void
vm_stack_growth(void *addr)
{
	/* 스택 영역은 프로그램 실행 중 생기는 빈 메모리를 담는 공간
		=> anonmous page
	*/
	vm_alloc_page(VM_ANON, pg_round_down(addr), true);
}

0개의 댓글