PintOS P3 #6 : Stack Growth
Project 2는 4KB로 다 해먹을 수 있지만, 우리는 이제 1MB 까지 즉, 256배에 달하는 크기까지 늘어날 수 있는 운영체제만의 스택 용량을 기대한다. 이를 만드는 과정은 생각보다 간단한데, 이 글을 통해서 도움을 받을 수 있을 것이다.
2025.06.09 - [구현하기] - PintOS P3 #5 : SPT를 비롯한 구현 #2
PintOS P3 #5 : SPT를 비롯한 구현 #2
한 제목에 P3 이면서 #5 이면서 #2 인게 참 헷갈리만도 하겠지만, 너가 다루고 있는 PintOS의 PML4는 이런 구조이다. Project 3 의 5번 글의 2번째 시리즈 글이라고 영역전개를 해야 슬슬 알아들을만하죠?
hyeonistic.tistory.com
이 글에서 이어진다.
언제 키움?
스택의 전체 크기가 늘어나야하냐 아니냐에 대해서의 기준을 세워야한다. 무턱대고 늘릴 수가 없다는 것. 어쨌든 성장 시키는 행위 자체는 기존에 setup_stack 과 별로 다르지 않다. 만약 직접 고민하길 바란다면 접은 글을 그냥 넘어가길 바란다. 아래에 있는건 실제 조건코드이다.
자연어로 설명된 스택 성장 조건은 다음과 같다.
기본적으로 주소는 유효해야한다. 그리고 현재 스택 포인터로부터 최대 허용 간격 이내에 있어야 한다.
또, 스택이 가질 수 있는 최대 크기를 초과해서도 안된다. 여기서 생각해야 할 것은 최대 허용 간격과 최대 크기의 개념은 다르다는 것.
마지막으로 스택은 높은 주소에서 낮은 방향으로 커지기 때문에, 주어지는 주소는 스택 시작 지점보다 아래 있어야한다.
static inline bool is_target_stack(void* rsp, void* addr) {
return addr != NULL
&& addr >= rsp - STACK_MAX_GAP
&& addr >= (void *)(USER_STACK - STACK_MAX_SIZE)
&& addr < (void *)USER_STACK;
}
이제 진짜 늘리기
어쨌든 우리는 기존에 제일 기초적인 스택을 설정하는 상황을 생각해보자. 그 과정은 setup_stack 함수에서 잘 달성해내고 있었다 :
static bool setup_stack (struct intr_frame *if_) {
bool success = false;
void *stack_bottom = (void *) (((uint8_t *) USER_STACK) - PGSIZE);
// SPT에 anon page로 등록
success = vm_alloc_page(VM_ANON, stack_bottom, true);
if (!success)
return false;
// 바로 프레임 할당(클레임)
success = vm_claim_page(stack_bottom);
if (!success)
return false;
// rsp를 USER_STACK으로 세팅
if_->rsp = USER_STACK;
return success;
}
SPT에 등록하고, 프레임 할당 하고, 당장 스택을 쓸 수 있는 형태로 가리키기 위해 시점을 조정해주는 것에 불과하다. 즉, 그냥 스택이라는 이름의 페이지일 뿐인 것이다.
우리는 해당 주소에 그냥 이어 붙이는 같은 타입의 페이지를 만들었다.
즉, 늘리기만 하고, 그 이후 접근에 대해서는 또 다른 영역에서 이뤄지게끔 하였다. 다른 조 내지 다른 사람들은 이러한 분기를 조금 다르게 한 사람들이 있던데, 일단 우리 조에서 일어나는 Page Fault의 흐름을 보면 :
// stack 확장에 있어 사용 할 기준 주소를 정하기 전에 어디서 왔는지를 알아내야한다.
// 그 이유는 커널과 유저 양쪽이 스택을 가지고 있기 떄문이다. 누가 낸 fault인지 확실히 하자는 것
void *rsp = (f==NULL) ? (thread_current()->rsp) : (f->rsp); // user access인 경우 rsp는 유저 stack을 가리킨다.
if (!user) // kernel access인 경우 thread에서 rsp를 가져와야 한다.
rsp = thread_current()->rsp;
page = spt_find_page(spt, addr);
if (page == NULL) {
// 스택 확장으로 처리할 수 있는 폴트인 경우
if (is_target_stack(rsp,addr)){
// vm_stack_growth()로 스택을 확장
vm_stack_growth(addr);
// 새 페이지를 얻어 claim
page = spt_find_page(spt, addr);
if (page == NULL)
return false;
}else{
// 스택 확장으로 안되는 건 어쩔 수 없다.
return false;
...
스택을 늘린다음 그 이후에 다시 페이지를 찾아오는 행위를 한번 더 시도한다.
이렇게 접근 했을 때 테스트 케이스들에 대해 별 다른 이상이 없었다.
static void vm_stack_growth (void *addr)
{
void *new_page_addr = pg_round_down(addr);
bool success = vm_alloc_page(VM_ANON, new_page_addr, true);
// vm_alloc_page_with_initializer의 5개 인자 중 좌측부터 3개만 채우고 사용 하는 것이다.
if (!success) PANIC("vm_stack_growth 실패!");
}
즉, 어떻게 보면 스택 성장이라는 명목은 그냥 기존 사용하던 스택 주소 주변에다가 다시 한번 페이지를 놓는 것에 불과한 것이다.
스택 성장에 관해선 이렇게 마무리 할 수 있다.