운영체제(구현) - 핀토스 - Virtual memory - Stack Growth

연도·2024년 6월 12일
0

운영체제 이론&구현

목록 보기
15/19

전체적인 그림

동작 원리

  1. stack growth 식별
  • 필요한 만큼 rsp를 이동

addr이 rsp보다 높은 주소를 가리켰을 때 stack 접근으로 판별

맨 처음 기준 rsp는 맨위에 있고, addr 즉 임의의 가상 주소로 조건문을 걸어서 vm_stack growth 식별하는거 걸러냄.

vm_try_handle_fault 이걸로 식별하기.

  1. stack growth 구현
  • 스택 크기를 증가시키기 위해 anonymous pages를 할당하여 주어진 addr이 더 이상 예외 주소가 되지 않도록 pg_round_down 으로 addr 주소를 업데이트해서 매핑된 영역을 키운다.

즉 이 과정이 stack growth이다. 어떤 함수 or 배열이 들어왔을 때 기존 남은 공간보다 더 필요할 때 rsp를 미리 내려서 내리기 전에 1MB보다 작은지 확인하고 내려서 그 사이에 addr이 들어왔을 때 그 기준으로 pg_round_down 이용해서 PGSIZE로 내림하여 처리한다.

해야할 것

  1. 접근한 주소가 stack 영역 내에 존재하는지 판별하기.
  • 핀토스에서는 최대 스택 크기 1MB제한. - USER_STACK - 1MB보다 낮은 주소를 가리키면 잘못된 접근으로 판단.
  • 1MB 안에 속했다 하더래도 rsp보다 높은 주소 값에 접근한 경우에만 ← rsp ≤ addr

운영체제가 실행되다가 signal에 의해서 프로세스를 중단시키면 실행되던 정보를 스택에 저장. 유저 프로그램이 스택 포인터 아래 스택에 데이터를 써놨다면, 이 때 데이터가 덮어씌어지면 변경될 수 있다. 즉 데이터의 무결성이 침범될 수 있다.

예외 상황)

rsp - 8에 접근했을 경우 PUSH 명령어가 실행되는 상황이다.

x86-64 PUSH 명령은 스택 포인터를 조정하기 전에 접근권한을 확인하므로, 스택포인트로부터 8바이트 아래에서 페이지 폴트가 발생할 수 있다.

  1. rsp 가져오기

함수 기준

  • vm_try_handle_fault - rsp를 이용해서 위에서 말했던 1MB안에 있는 경우와 위에 있는 경우, rsp-8인 경우를 생각해서 조건문을 걸어 함수 수정.

  • vm_stack_growth - 위에 핸들러에서 이 함수를 호출하여서 addr을 더 이상 예외 주소가 되지 않도록 한다.

기초 작업

  1. 커널 모드로 전환될 때 현재 유저 스택 포인터를 저장해두기 위한 필드를 스레드 구조체에 추가

코드

include/threads/thread.h 

#ifdef VM
	/* Table for whole virtual memory owned by thread. */
	struct supplemental_page_table spt;
	void *rsp; // 현재 유저 스택의 스택 포인터를 저장해두기 위한 필드를 thread 구조체에 추가.
#endif
  1. syscall_handler 함수에서 스택 포인터 저장.
#ifdef VM
		thread_current()->rsp = f->rsp; // 3-3 sg시 구현
#endif

본 작업

vm_try_handle_fault - stack_growth 식별하기

가상주소가 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);
		}

vm_stack_growth 구현

스택 크기를 증가 시키기 위해 어나니머스 페이지를 할당하여 주어진 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);
}

출처 : https://e-juhee.tistory.com/entry/Pintos-KAIST-Project-3-Stack-Growth-Pintos%EC%9D%98-%EC%8A%A4%ED%83%9D-%ED%99%95%EC%9E%A5-%EB%A9%94%EC%BB%A4%EB%8B%88%EC%A6%98

0개의 댓글