[SW정글 79일차] pintos project3 p3. stack growth

rg.log·2022년 12월 6일
0

SW 사관학교 JUNGLE

목록 보기
20/31

git book

프로젝트2에서, 스택은 USER_STACK부터 시작하는 하나의 페이지였으며, 이 사이즈에 맞춰 프로그램의 실행이 제한적이었습니다.
이제 스택이 현재 크기보다 크게 자라나면, 우리는 추가적으로 필요한 페이지들을 할당할 것입니다.

여기까지는 기본적인 정의 stack growth를 만들게 된 이유라 할 수 있다.

사용자 프로그램이 스택 포인터 아래의 스택에 쓰면 버그가 발생하는데, 이는 일반적인 실제 OS는 스택의 데이터를 수정하는 "신호"를 전달하기 위해, 언제든지 프로세스를 중단할 수 있기 때문입니다.
그러나 x86-64의 PUSH 인스트럭션은 스택 포인터를 수정하기 전에 액세스의 권한을 체크하므로, 따라서 스택 포인터 아래 8바이트에 페이지 오류가 발생할 수 있습니다.

만일 rsp가 stack page의 경계에 걸쳐있을 때, push instruction은 rsp보다 8byte 아래의 주소에 접근을 시도하고 page fault가 발생하게 된다. 일반적으로 rsp보다 아래 주소에 접근하는 건 bug지만 push instruction에 의한 경우는 bug case로 취급하지 않고 구분해야 한다.

sub에 의한 rsp의 이동은 단순 산술연산 명령어(arithmetic instruction)로, memory에 대한 access가 아니므로 page fault를 발생시키지 않는다.

반면, page fault는 rsp가 미할당된 stack region상에서 memory access를 시도할 때만 (push/pop, indirect memory reference, call/ret 등) 발생한다.

코드로 예를 들어보자. 아래는 test case 중 pt-grow-stack 코드이다.

void
test_main (void)
{
  char stack_obj[4096];
  struct arc4 arc4;

  arc4_init (&arc4, "foobar", 6);
  memset (stack_obj, 0, sizeof stack_obj);
  arc4_crypt (&arc4, stack_obj, sizeof stack_obj);
  msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj));
}

char stack_obj[4096];
struct arc4 arc4;
이들의 선언으로 rsp가 한번에 쭉- 내려가고 옮겨진 rsp의 8byte 아래의 주소에 접근을 시도하는 PUSH 인스트럭션으로 인한 page fault가 일어난다.

이 공간은 test_main 함수의 local variable을 위한 공간으로 arc4_init, memset, arc4_crypt, cksum등의 함수에 포인터로 전달되어 사용된다.

당신은 현재 유저 프로그램의 스택 포인터의 값을 얻을 수 있어야합니다.
유저 프로그램에 의해 발생된 페이지 폴트 또는 시스템 콜 내에서, syscall_handler() 또는 page_fault()에게 각각 전달된 struct intr_frame의 rsp 멤버로부터 그 값을 얻을 수 있습니다.
당신이 유효하지 않은 메모리 액세스를 탐지하기 위해 페이지폴트에 의지한다면 커널에서 발생하는 페이지 폴트의 경우 또한 제어해야합니다.
프로세서는 예외(exception)가 유저 모드에서 커널 모드로의 변경을 일으킬때만 스택 포인터를 저장하기 때문에, page_fault()에 전달된 struct intr_frame으로부터 rsp 를 읽어들이는 것은 스택 포인터가 아니라 다른 정의되지 않은 값을 생성하게 될 수 있습니다.
당신은 유저모드에서 커널모드로의 최초의 전환에서 rsp 를  struct thread에 저장하는 등의 새로운 방법을 마련해야합니다.

"커널에서 발생하는 page fault의 경우 또한 제어해야 한다." 는 해당 부분을 이해하기 위해 팀원과 고민을 하고 공부를 해보다 결국 다른 팀원들에게도 물어보고 해결을 얻었다.

addr이 더이상 잘못된 주소가 아니도록 하나 또는 그 이상의 익명 페이지를 할당하여 스택의 크기를 증가시킵니다.
할당을 제어할 때, addr을 PGSIZE으로 round down 시키는 것을 잊지 마십시오.

해당 부분에서 우리는 vm_try_handle_fault로 들어온 fault_addr를 round down(fault_addr-rsp) 후 PGSIZE로 나누어 나오는 개수만큼 while문을 돌면서 page를 할당한 후 물리 frame과 연결해주는 claim을 해주고자 하였다.

반면 fault_addr부터 한 페이지를 할당해주고, 그안에 내용을 채우고 주소가 내려오면서 fault날 때마다 한 페이지씩 할당해주는 팀도 있었다.

대부분의 OS들은 스택 크기에 절대적인 제한을 둡니다.
몇몇 OS는 유저가 수정할 수 있는 양을 제한합니다. (많은 유닉스 시스템에서 ulimit 명령어와 함께.)
많은 GNU/Linux 시스템에서, 디폴트 리밋은 8MB입니다.
이 프로젝트에서, 당신은 스택 사이즈를 최대 1MB로 제한해야합니다.

이런 부분이 나오면 기존 OS와 명확하게 비교를 하고 싶어진다. pintos는 80x86 아키텍처를 위한 간단한 운영 체제 프레임 워크이다. OS는 우선 스킵.


오늘의 나는

요즘 하루하루 upstream으로 팀 레포를 사용하면서 알음알음 알아가는 깨알 깃팁이 있다.

💡 깨알 깃팁
git commit --amend -m "이전 커밋 메세지와 다르게 수정하고 싶은 내용" 을 통해 바로 이전 커밋 메세지를 수정할 수 있다.

우리조는 printf를 사용해 디버깅을 진행하고 있다. GDB를 통해 도움받은 기억이 없기도 하고, printf가 직관적이고 빠르기 때문이다. 이게 맞나? 하는 생각이 들지만 현재의 최선으로 진행해보고자 하고 좋은 후기가 있다면 언제든 환영이다!

0개의 댓글