전체적인 그림
동작 원리
addr이 rsp보다 높은 주소를 가리켰을 때 stack 접근으로 판별
맨 처음 기준 rsp는 맨위에 있고, addr 즉 임의의 가상 주소로 조건문을 걸어서 vm_stack growth
식별하는거 걸러냄.
즉 vm_try_handle_fault
이걸로 식별하기.
pg_round_down
으로 addr 주소를 업데이트해서 매핑된 영역을 키운다.즉 이 과정이 stack growth이다. 어떤 함수 or 배열이 들어왔을 때 기존 남은 공간보다 더 필요할 때 rsp를 미리 내려서 내리기 전에 1MB보다 작은지 확인하고 내려서 그 사이에 addr이 들어왔을 때 그 기준으로 pg_round_down
이용해서 PGSIZE로 내림하여 처리한다.
해야할 것
운영체제가 실행되다가 signal에 의해서 프로세스를 중단시키면 실행되던 정보를 스택에 저장. 유저 프로그램이 스택 포인터 아래 스택에 데이터를 써놨다면, 이 때 데이터가 덮어씌어지면 변경될 수 있다. 즉 데이터의 무결성이 침범될 수 있다.
예외 상황)
rsp - 8에 접근했을 경우 PUSH 명령어가 실행되는 상황이다.
x86-64 PUSH 명령은 스택 포인터를 조정하기 전에 접근권한을 확인하므로, 스택포인트로부터 8바이트 아래에서 페이지 폴트가 발생할 수 있다.
함수 기준
vm_try_handle_fault - rsp를 이용해서 위에서 말했던 1MB안에 있는 경우와 위에 있는 경우, rsp-8인 경우를 생각해서 조건문을 걸어 함수 수정.
vm_stack_growth - 위에 핸들러에서 이 함수를 호출하여서 addr을 더 이상 예외 주소가 되지 않도록 한다.
코드
include/threads/thread.h
#ifdef VM
/* Table for whole virtual memory owned by thread. */
struct supplemental_page_table spt;
void *rsp; // 현재 유저 스택의 스택 포인터를 저장해두기 위한 필드를 thread 구조체에 추가.
#endif
#ifdef VM
thread_current()->rsp = f->rsp; // 3-3 sg시 구현
#endif
가상주소가 rsp보다 높을 때 스택 접근으로 판별, 그 아래 일 경우는 PUSH 명령어를 통해 rsp-8에 접근한 경우에 stack growth로 해결한다.
코드
void *rsp = f->rsp; // user access인 경우 rsp는 유저 스택을 가리킴
if (!user)
{
rsp = thread_current()->rsp;
}
/* 스택 확장으로 처리할 수 있는 폴트인 경우, vm_stack_growth 호출 */
// 스택 확장 가능 범위 인지 and 폴트가 발생한 주소가 스택확장으로 해결 가능한지 and 폴트가 발생한 주소가 사용자 스택 범위 안에 있는지
if (USER_STACK - (1 << 20) <= rsp - 8 && rsp - 8 == addr && addr <= USER_STACK)
{
vm_stack_growth(addr);
}
// 위에랑 차이점은 현재 가운데 - 현재 스택 포인터보다 높은 주소 체크
else if (USER_STACK - (1 << 20) <= rsp && rsp <= addr && addr <= USER_STACK)
{
vm_stack_growth(addr);
}
스택 크기를 증가 시키기 위해 어나니머스 페이지를 할당하여 주어진 addr이 더 이상 예외 주소가 되지 않도록 함. 할당 할 때 addr을 PGSIZE로 내림하여 처리.
코드
/* Growing the stack. */
// 주어진 주소가 더 이상 예외 주소가 되지 않도록 스택의 크기를 증가시키기 위해 anon page를 할당.
static void
vm_stack_growth (void *addr UNUSED) {
/*
todo : 스택 크기를 증가시키기 위해 anon page를 하나 이상 할당하여 주어진 주소(addr)가 더 이상 예외 주소(faulted address)가 되지 않도록 한다.
todo : 할당할 때 addr을 **PGSIZE**로 **내림**하여 처리. 스택은 위 > 아래
*/
vm_alloc_page(VM_ANON | VM_MARKER_0, pg_round_down(addr), 1);
}