[PintOS] Project 3 - Stack Growth

novxerim·2022년 1월 27일
0

SW-Jungle

목록 보기
45/59

- Stack Growth

프로젝트 2에서 스택은 USER_STAK부터 시작하는 단일 페이지였으며, 프로그램의 실행은 이 크기로 제한되었습니다. 이제 스택이 현재 크기를 초과하여 커지면, 필요에 따라 추가 페이지를 할당합니다.

추가 페이지가 스택 액세스로 "표시(appear)"되는 경우에만 페이지를 할당합니다. 스택 액세스를 다른 액세스와 구별하는 경험적 접근 방법(heuristic)을 구상합니다.

사용자 프로그램들이 스택 포인터 아래의 스택에 쓸 경우 버그가 발생하는데, 그 이유는 전형적인 실제 OS들이 스택의 데이터를 수정하는 "신호(signal)"를 전달하기 위해 프로세스를 언제든지 중단할 수 있기 때문입니다. 그러나 x86-64 PUSH 명령은 스택 포인터를 조정하기 전에 액세스 권한을 확인하므로 스택 포인터보다 8바이트 아래에 페이지 오류가 발생할 수 있습니다.

사용자 프로그램의 스택 포인터의 현재 값을 얻을 수 있어야 합니다. 시스템 호출 또는 사용자 프로그램에 의해 생성된 페이지 장애 내에서 syscall_handler() 또는 page_fault()로 전달된 구조의 rsp 멤버에서 각각 이를 검색할 수 있습니다. 잘못된 메모리 액세스를 탐지하기 위해 페이지 폴트를 의존(사용)하는 경우, 커널에서 페이지 폴트가 발생하는 다른 경우를 처리해야 합니다.

예외로 인해 사용자 모드에서 커널 모드로 전환되는 경우에만 프로세서가 스택 포인터를 저장하기 때문에, page_fault()에 전달된 struct intr_frame에서 rsp를 읽으면 사용자 스택 포인터가 아닌 정의되지 않은 값이 생성됩니다. 사용자 모드에서 커널 모드로 처음 전환할 때 rspstruct thread에 저장하는 것과 같은 다른 방법을 준비해야 합니다.

vm_try_handle_fault : void *rsp_stack = is_kernel_vaddr(f->rsp) ? thread_current()->rsp_stack : f->rsp;

.
stack growth(스택 증가) 기능을 구현합니다. 이를 구현하려면 먼저 vm/vm.c에서 vm_try_handle_fault를 수정하여 스택 증가를 식별합니다.
스택 증가를 식별한 후에는 vm/vm.cvm_stack_growth를 호출하여 스택을 확장해야 합니다. vm_stack_growth를 구현하세요.


- 구현1.

bool vm_try_handle_fault (struct intr_frame *f, void *addr,
bool user, bool write, bool not_present);

이 함수는 페이지 폴트 예외를 처리하는 동안 userprog/exception.c에서 page_fault로 호출됩니다.
이 함수에서는 페이지 폴트가 stack growth에 유효한 경우인지 아닌지(이 페이지폴트가 stack growth와 관련된 페이지 폴트인지) 확인해야 합니다. stack growth가 페이지폴트를 처리할 수 있음을 확인한 경우, 페이지 폴트가 발생한 주소로 vm_stack_growth를 호출합니다.

  • 코드
// [exception.c]
/* Return true on success */
bool
vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED,
		bool user UNUSED, bool write UNUSED, bool not_present UNUSED) {
	struct supplemental_page_table *spt UNUSED = &thread_current ()->spt;
	struct page *page = NULL;
	/* TODO: Validate the fault */
	/* TODO: Your code goes here */

	// return vm_do_claim_page (page);
	// Step 1. Locate the page that faulted in the supplemental page table
	void * fpage_uvaddr = pg_round_down(addr); // round down to nearest PGSIZE
	// void * fpage_uvaddr = (uint64_t)addr - ((uint64_t)addr%PGSIZE); // round down to nearest PGSIZE

	struct page *fpage = spt_find_page(spt, fpage_uvaddr);
	
	// Invalid access - Not in SPT (stack growth or abort) / kernel vaddr / write request to read-only page
	if(is_kernel_vaddr(addr)){

		return false;
	}
	else if (fpage == NULL){
		void *rsp = user ? f->rsp : thread_current()->rsp; // a page fault occurs in the kernel
		const int GROWTH_LIMIT = 32; // heuristic
		const int STACK_LIMIT = USER_STACK - (1<<20); // 1MB size limit on stack

		// Check stack size max limit and stack growth request heuristically
		if((uint64_t)addr > STACK_LIMIT && USER_STACK > (uint64_t)addr && (uint64_t)addr > (uint64_t)rsp - GROWTH_LIMIT){
			vm_stack_growth (fpage_uvaddr);
			fpage = spt_find_page(spt, fpage_uvaddr);
		}
		else{

			exit(-1); // mmap-unmap
			//return false;
		}
	}
	else if(write && !fpage->writable){

		exit(-1); // mmap-ro
		// return false;
	}

	ASSERT(fpage != NULL)

	// Step 2~4.
	bool gotFrame = vm_do_claim_page (fpage);

	// if (gotFrame)
		// list_push_back(&frame_table, &fpage->frame->elem);

	return gotFrame;
}
  • 추가
// [include>threads>thread.h]
// struct thread의 #ifdef VM - #endif 밑에 추가
            
// Project 3-2 stack growth
uint64_t rsp; // a page fault occurs in the kernel

- 구현2.

void vm_stack_growth (void *addr);

addr가 더 이상 오류 주소가 되지 않도록 하나 이상의 익명 페이지를 할당하여 스택 크기를 늘립니다. 할당을 처리할 때 PGSIZE 로 addr를 내림해야 합니다.

대부분의 OS들은 스택 크기에 대한 절대적인 제한을 부과합니다. 일부 OS는 예를 들어 많은 Unix 시스템에서 ulimit 명령을 사용하여 사용자가 조정할 수 있도록 제한을 설정합니다. 많은 GNU/Linux 시스템에서 기본 제한은 8MB입니다. 이 프로젝트의 경우 스택 크기를 최대 1MB로 제한해야 합니다.

  • 코드
/* Growing the stack. */
static void
vm_stack_growth (void *addr UNUSED) {
	vm_alloc_page(VM_ANON | VM_MARKER_0, addr, true); // Create uninit page for stack; will become anon page
	//bool success = vm_claim_page(addr);
}

이제 모든 stack-growth의 테스트 케이스를 통과해야 합니다.

- 수정사항

container 추가, [process.c]에 setup_stack 함수 #ifdef VM 부분 맨 아래에 구현 안해놨던 것 구현해줌.

syscall.c의 check_address등 바꿔줘야 함


  • 여기까지 결과 (40 of 141 tests failed.)
    pass tests/userprog/args-none
    pass tests/userprog/args-single
    pass tests/userprog/args-multiple
    pass tests/userprog/args-many
    pass tests/userprog/args-dbl-space
    pass tests/userprog/halt
    pass tests/userprog/exit
    pass tests/userprog/create-normal
    pass tests/userprog/create-empty
    pass tests/userprog/create-null
    pass tests/userprog/create-bad-ptr
    pass tests/userprog/create-long
    pass tests/userprog/create-exists
    pass tests/userprog/create-bound
    pass tests/userprog/open-normal
    pass tests/userprog/open-missing
    pass tests/userprog/open-boundary
    pass tests/userprog/open-empty
    pass tests/userprog/open-null
    pass tests/userprog/open-bad-ptr
    pass tests/userprog/open-twice
    pass tests/userprog/close-normal
    pass tests/userprog/close-twice
    pass tests/userprog/close-bad-fd
    pass tests/userprog/read-normal
    pass tests/userprog/read-bad-ptr
    pass tests/userprog/read-boundary
    pass tests/userprog/read-zero
    pass tests/userprog/read-stdout
    pass tests/userprog/read-bad-fd
    pass tests/userprog/write-normal
    pass tests/userprog/write-bad-ptr
    pass tests/userprog/write-boundary
    pass tests/userprog/write-zero
    pass tests/userprog/write-stdin
    pass tests/userprog/write-bad-fd
    pass tests/userprog/fork-once
    pass tests/userprog/fork-multiple
    pass tests/userprog/fork-recursive
    **FAIL tests/userprog/fork-read
    FAIL tests/userprog/fork-close
    FAIL tests/userprog/fork-boundary**
    pass tests/userprog/exec-once
    pass tests/userprog/exec-arg
    **FAIL tests/userprog/exec-boundary**
    pass tests/userprog/exec-missing
    pass tests/userprog/exec-bad-ptr
    **FAIL tests/userprog/exec-read**
    pass tests/userprog/wait-simple
    pass tests/userprog/wait-twice
    pass tests/userprog/wait-killed
    pass tests/userprog/wait-bad-pid
    pass tests/userprog/multi-recurse
    pass tests/userprog/multi-child-fd
    pass tests/userprog/rox-simple
    pass tests/userprog/rox-child
    pass tests/userprog/rox-multichild
    pass tests/userprog/bad-read
    pass tests/userprog/bad-write
    pass tests/userprog/bad-read2
    pass tests/userprog/bad-write2
    pass tests/userprog/bad-jump
    pass tests/userprog/bad-jump2
    pass tests/vm/pt-grow-stack
    pass tests/vm/pt-grow-bad
    pass tests/vm/pt-big-stk-obj
    pass tests/vm/pt-bad-addr
    pass tests/vm/pt-bad-read
    pass tests/vm/pt-write-code
    **FAIL tests/vm/pt-write-code2
    FAIL tests/vm/pt-grow-stk-sc**
    pass tests/vm/page-linear
    **FAIL tests/vm/page-parallel
    FAIL tests/vm/page-merge-seq
    FAIL tests/vm/page-merge-par
    FAIL tests/vm/page-merge-stk
    FAIL tests/vm/page-merge-mm**
    pass tests/vm/page-shuffle
    **FAIL tests/vm/mmap-read
    FAIL tests/vm/mmap-close
    FAIL tests/vm/mmap-unmap
    FAIL tests/vm/mmap-overlap
    FAIL tests/vm/mmap-twice
    FAIL tests/vm/mmap-write
    FAIL tests/vm/mmap-ro
    FAIL tests/vm/mmap-exit
    FAIL tests/vm/mmap-shuffle**
    pass tests/vm/mmap-bad-fd
    **FAIL tests/vm/mmap-clean
    FAIL tests/vm/mmap-inherit
    FAIL tests/vm/mmap-misalign
    FAIL tests/vm/mmap-null
    FAIL tests/vm/mmap-over-code
    FAIL tests/vm/mmap-over-data
    FAIL tests/vm/mmap-over-stk
    FAIL tests/vm/mmap-remove**
    pass tests/vm/mmap-zero
    pass tests/vm/mmap-bad-fd2
    pass tests/vm/mmap-bad-fd3
    pass tests/vm/mmap-zero-len
    **FAIL tests/vm/mmap-off
    FAIL tests/vm/mmap-bad-off
    FAIL tests/vm/mmap-kernel
    FAIL tests/vm/lazy-file**
    pass tests/vm/lazy-anon
    **FAIL tests/vm/swap-file
    FAIL tests/vm/swap-anon
    FAIL tests/vm/swap-iter
    FAIL tests/vm/swap-fork**
    pass tests/filesys/base/lg-create
    pass tests/filesys/base/lg-full
    pass tests/filesys/base/lg-random
    pass tests/filesys/base/lg-seq-block
    pass tests/filesys/base/lg-seq-random
    pass tests/filesys/base/sm-create
    pass tests/filesys/base/sm-full
    pass tests/filesys/base/sm-random
    pass tests/filesys/base/sm-seq-block
    pass tests/filesys/base/sm-seq-random
    **FAIL tests/filesys/base/syn-read**
    pass tests/filesys/base/syn-remove
    **FAIL tests/filesys/base/syn-write**
    pass tests/threads/alarm-single
    pass tests/threads/alarm-multiple
    pass tests/threads/alarm-simultaneous
    pass tests/threads/alarm-priority
    pass tests/threads/alarm-zero
    pass tests/threads/alarm-negative
    pass tests/threads/priority-change
    pass tests/threads/priority-donate-one
    pass tests/threads/priority-donate-multiple
    pass tests/threads/priority-donate-multiple2
    pass tests/threads/priority-donate-nest
    pass tests/threads/priority-donate-sema
    pass tests/threads/priority-donate-lower
    pass tests/threads/priority-fifo
    pass tests/threads/priority-preempt
    pass tests/threads/priority-sema
    pass tests/threads/priority-condvar
    pass tests/threads/priority-donate-chain
    **FAIL tests/vm/cow/cow-simple**
    40 of 141 tests failed.

profile
블로그 이전했습니다. https://yerimi11.tistory.com/

0개의 댓글