프로젝트 2에서는 스택이 USER_STACK
에서 시작하는 단일 페이지였으며, 프로그램의 실행은 이 크기로 제한되었습니다. 이제 스택이 현재 크기를 초과하여 증가하면 필요에 따라 추가 페이지를 할당합니다.
스택 접근으로 "보이는" 경우에만 추가 페이지를 할당하세요. 스택 접근과 다른 접근을 구분하려는 휴리스틱을 고안하세요.
일반적인 실제 OS는 언제든지 프로세스를 중단하여 스택의 데이터를 수정하는 "신호"를 전달할 수 있기 때문에, 사용자 프로그램이 스택 포인터 아래의 스택에 쓰면 버그가 있는 것입니다. 그러나 x86-64 PUSH 명령은 스택 포인터를 조정하기 전에 접근 권한을 확인하므로 스택 포인터 아래 8바이트에서 페이지 폴트를 일으킬 수 있습니다.
사용자 프로그램의 스택 포인터 현재 값을 얻을 수 있어야 합니다. 시스템 콜이나 사용자 프로그램에 의해 생성된 페이지 폴트 내에서는 syscall_handler()
또는 page_fault()
에 전달된 struct intr_frame
의 rsp
멤버에서 검색할 수 있습니다. 잘못된 메모리 접근을 감지하기 위해 페이지 폴트에 의존하는 경우, 커널에서 페이지 폴트가 발생하는 또 다른 경우를 처리해야 합니다. 프로세서는 예외로 인해 사용자 모드에서 커널 모드로 전환될 때만 스택 포인터를 저장하므로 page_fault()
에 전달된 struct intr_frame
에서 rsp
를 읽으면 사용자 스택 포인터가 아닌 정의되지 않은 값이 생성됩니다. 사용자에서 커널 모드로의 초기 전환 시 struct thread
에 rsp를 저장하는 등의 다른 방법을 마련해야 합니다.
스택 성장 기능 구현하기 이를 구현하기 위해 먼저 vm/vm.c
의 vm_try_handle_fault
함수를 수정하여 스택 성장을 식별합니다. 스택 성장을 식별한 후에는 vm/vm.c
의 vm_stack_growth
함수를 호출하여 스택을 증가시켜야 합니다. vm_stack_growth
함수를 구현하세요.
bool vm_try_handle_fault (struct intr_frame *f, void *addr,
bool user, bool write, bool not_present);
이 함수는
userprog/exception.c
의page_fault
함수에서 페이지 폴트 예외를 처리하는 동안 호출됩니다. 이 함수에서는 페이지 폴트가 스택 성장에 대해 유효한 경우인지 확인해야 합니다. 폴트가 스택 성장으로 처리될 수 있다고 확인되면 폴트가 발생한 주소와 함께vm_stack_growth
를 호출하세요.
void vm_stack_growth (void *addr);
addr
이 더 이상 폴트가 발생한 주소가 아니도록 하나 이상의 익명 페이지를 할당하여 스택 크기를 증가시킵니다. 할당을 처리할 때addr
을 PGSIZE로 내림하여 정렬해야 합니다.
대부분의 운영 체제는 스택 크기에 대해 절대적인 제한을 둡니다. 일부 운영 체제는 이 제한을 사용자가 조정할 수 있도록 합니다. 예를 들어 많은 Unix 시스템에서 ulimit
명령을 사용합니다. 많은 GNU/Linux 시스템에서 기본 제한은 8MB입니다. 이 프로젝트에서는 스택 크기를 최대 1MB로 제한해야 합니다.
이제 모든 스택 성장 테스트 케이스를 통과해야 합니다.