PintOS 3주차 -day 15

솔다·2023년 1월 13일
0

힘겹게 진행하는 3주차에서 PintOS에 치이다가 이제서야 이어서 포스팅을 올린다. 그동안 많은 변화가 있었다. 지난번에 이어졌던 문제상황도 포스팅하고 몇가지만 추가로 적어서 기록하고자 한다.

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 */
	if (is_kernel_vaddr(addr)){
		return false;
	}

	if (not_present)
	{
		page = spt_find_page(spt, addr);
		if (page == NULL)
			return false;
		if(!vm_do_claim_page(page))
			return false;
	}

	return true;
}

일단 지난번에 무한루프에 빠졌던 이유는, not_present 대신에 write을 썼기 때문에 그랬다. if(write)에서 검사를 하고, 실제 페이지 할당을 위해서 vm_do_claim_page를 실행해야 하는데, not_present 여부(페이지가 할당되어 있는지)를 확인하지 않고, write에서부터 걸러져서 문제가 발생했기 때문. fault_handler를 거치고 나서도 다시 똑같은 주소에 접근했더니, 계속 무한루프를 도는 것.

이 문제를 해결 하고 나서도 많은 문제가 있었다. 특히 유저 스택을 설정해주는 부분에서 많은 문제가 발생했는데, 이 부분이 가장 어려웠다.

가장 오랫동안 붙잡고 있던 test 케이스는 큰 데이터를 스택 영역에 할당해주는 것이었다.

해당 부분에서 우리가 가지고 있던 의문점은 printf를 찍으면서, 레지스터의 rsp값. 즉 스택 포인터의 값은 계속해서 변하는데, 접근해서 조회하려고 하는 addr 에서 계속 page fault가 된다는 것이었다. 그렇게 계속해서 page fault 가 발생하다가 테스트 케이스가 강제로 종료되던 것.

처음에는 스택할당을 위해서 사용해야 하는 포인터를 잘못 쓰는 것인지 고민했다. 특히 유저스택과 커널스택을 가리키는 포인터의 validation을 체크 했었는데 이것만으로는 부족했다. 완전히 다른 영역을 가리키는 포인터가 있었던 것. Gitbook을 다시 확인해보는 것을 통해서 우리는 해결책을 찾을 수 있었다.

커널영역에서 page fault 가 발생했을 때, 이를 intr_frame에 저장하는 프로세스가 없으므로, 문제가 발생했던 것.

그래서 Thread에서 커널로 transition이 일어났을 때 가장 마지막에 thread 구조체에 값을 복사해서 저장해 두었던 rsp값을 가져와서 세팅해주었다.

삼항 연산자를 이용해서 세팅해주었는데, 정상적으로 테스트는 통과했다. 아래는 위의 vm_try_fault_handler를 이에 맞춰서 수정한 코드다.

bool
vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED,
		bool user UNUSED, bool write UNUSED, bool not_present UNUSED) {
		
	struct thread *t_curr = thread_current();
	struct supplemental_page_table *spt UNUSED = &t_curr->spt;

	// /* save stack pointer for stack_growth */
	struct page *page = NULL;
	page = spt_find_page(spt, addr);

	/* TODO: Your code goes here */
	/* TODO: Validate the fault */
	if (is_kernel_vaddr(addr))
		return false;

	if (!addr)
		return false;

	uintptr_t rsp = user ? f->rsp : t_curr->stack_pointer;
	if (not_present) {	
		if (page == NULL) {
			if (write && (addr >= rsp - 8) && (addr < USER_STACK) && addr > STACK_LIMIT) {
				vm_stack_growth(addr);
				return true;
    		}
			else {
				return false;
			}
		}

		if (!vm_claim_page (addr)) {
			return false;
		}
	}
	
	if (write && !page->writable)
		return false;

	return true;
}

처음에는 포인터가 왜 유효하지 않은지 이해가 안되서 머리가 아팠는데, 인터넷을 통해서 전에 핀토스를 했던 사람이 포인터에 대해 정리해놓은 개념을 찾아보고 읽은게 큰 도움이 되었다. 근데 아직 전체 과제 중에서 3/5 밖에 진행이 안되어서 할일이 많이 남았다. 프로젝트2 까지는 진도에 맞출 수 있었는데, 3은 제자리 걸음을 걷는일이 많아서 완성을 다 못할 수도 있을 것 같다. 그래도 남은 기간 열심히 해봐야지...

0개의 댓글