userprog 테스트를 완료하기 위해선, load 함수를 작성해야한다. VM이 활성화되면서, 2개 함수(load_segment, setup_stack)을 다시 구현해야한다.
유저가 프로그램을 실행하면 main 함수가 실행된다. main 함수는 스택을 기대하며, 그 안에는 argc, argv가 들었다고 가정한다. setup_stack은 argc, argv를 스택에 저장하기 위해 스택 공간을 만들어주는 역할이다.
project3에서는 VM 을 사용하도록 변경되었다. VM을 사용한다는 것은 spt에 페이지를 등록하고, 이 정보를 이용해 메모리를 관리하겠다는걸 의미한다.
차이가 명확해지기 위해 proejct2와 비교해보자. project2에서는 page table(pml4)가 있었다. 그러나 spt가 없었고, 지금처럼 page 구조체도 사용하지 않았다. 당시는 eager loading 방식이였고, 스택을 만들때 프레임을 할당한 뒤 pml4에 직접 등록했다.
static bool
setup_stack(struct intr_frame *if_)
{
// 8비트 정수
uint8_t *kpage;
bool success = false;
// 페이지 할당
kpage = palloc_get_page(PAL_USER | PAL_ZERO);
if (kpage != NULL)
{
// 커널에서 가져온(kpage) 수정할 수 있는(true) 페이지를 첫번째 인자에 매핑
success = install_page(((uint8_t *)USER_STACK) - PGSIZE, kpage, true);
if (success)
// 인터럽트 프레임(if)에서 rsp(스택 포인터)를 USER_STACK으로 변경
if_->rsp = USER_STACK;
//// 프로그램 시작 시 사용할 스택의 시작위치 저장
else
palloc_free_page(kpage);
}
return success;
}
그러나 VM을 사용한다는것은 메모리를 다룰 때, 페이지를 이용하겠다는것이다. project3에서는 페이지를 이용해 lazy loading까지 구현하는게 목표다. 그러나, setup 스택은 메인 함수가 실행되자마자 사용될 공간이미 즉시 할당하면 된다.
추가된 작업들을 정리하면 아래와 같다.
[project2]
frame 할당 및 초기화
pml4에 (va, pa) 등록
[project3]
frame 할당 및 초기화
pml4에 (va, pa) 등록
페이지 구조체 생성
spt 등록
swap_in/out
stack growth
destroy
대략적으로 나타내면 아래 순서대로 하면 된다.
정확하지 않을 수 있지만, 나타내면 아래같은 흐름이 아닐까 싶다
static bool
setup_stack(struct intr_frame *if_)
{
bool success = false;
void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE);
struct supplemental_page_table* spt = &thread_current()->spt;
ASSERT(pg_ofs(stack_bottom) == 0);
// 페이지 할당 및 uninit으로 초기화
if (!vm_alloc_page_with_initializer (VM_ANON | VM_MARKER_0, stack_bottom,
true, NULL, NULL)) {
goto done;
}
// 프레임 할당, pml4등록 및 초기화
if (!vm_claim_page(stack_bottom)) {
goto done;
}
// rsp 세팅 의문인건 rsp를 stack_bottom으로 보내지 않아도 되는건가? 이건 뒤에서 하는건가?
if_->rsp = USER_STACK;
success = true;
done:
return success;
}