(TIL)하루하루 정신 나갈 것 같은 pintos

낚시하는 곰·2025년 6월 5일
1

jungle TIL

목록 보기
9/20

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

구상

rsp - 8을 check 한다.

addr은 rsp보다 높은 주소에 있어야 한다. rsp는 stack_growth()가 실행되면서 page down되기 되문에 한 개의 page만큼 먼저 이동해 있기 때문이다. → 여기서 page fault()가 발생하는 것 같은데?

여기서 page fault가 발생함. 따라서 이 위치에 page가 존재하지 않는다는 거니까 lazy load 방식으로 공간 할당

*rsp = current_thead()->rsp;
if -> addr >= rsp - 8 && addr <= stack_bottom

stack_bottom을 어떻게 계산하지??

stack_bottom = USER_STACK - 1MB

위 조건을 통과한다면 stack에 4KB 공간을 할당해줘야 한다.

bool result = vm_alloc_page_with_initializer(VM_ANON | VM_MARKER_STACK, addr, true, NULL, NULL);

마지막으로 page만큼 rsp를 아래로 이동시켜야 한다.

git book 조건을 보면

이때 addr은 반드시 페이지 단위(PGSIZE)로 내림(round down) 처리 후 할당해야 합니다.

라는 조건이 명시되어 있기 때문이다.

구현

// 페이지 폴트 처리

// 보충 페이지 테이블의 가장 중요한 사용자는 페이지 폴트 핸들러입니다. 프로젝트 2에서는 페이지 폴트가 항상 커널 또는 사용자 프로그램에서 버그를 의미했습니다.
// 그러나 프로젝트 3에서는 이제 그렇지 않습니다. 이제 페이지 폴트는 단순히 페이지를 파일이나 스왑 슬롯에서 가져와야 함을 나타냅니다. 이 경우를 처리하기 위해 더 정교한 페이지 폴트 핸들러를 구현해야 합니다.
// 페이지 폴트 핸들러인 page_fault() (userprog/exception.c)는 vm_try_handle_fault()라는 함수(이 함수는 vm/vm.c에 있음)를 호출하여 이를 처리합니다. 이 페이지 폴트 핸들러는 대체로 다음과 같이 해야 합니다:

// 	1.	보충 페이지 테이블에서 폴트가 발생한 페이지 찾기
// 메모리 참조가 유효한 경우, 보충 페이지 테이블 항목을 사용하여 페이지에 들어갈 데이터를 찾습니다. 데이터는 파일 시스템, 스왑 슬롯에 있을 수 있으며, 아예 0으로 채워진 페이지일 수도 있습니다.
// 공유(예: Copy-on-Write)를 구현하면 페이지의 데이터가 이미 페이지 프레임에 있을 수도 있지만, 페이지 테이블에는 없을 수 있습니다. 보충 페이지 테이블이 사용자 프로세스가 해당 주소에서 데이터를 기대해서는 안 된다고 나타내거나,
// 페이지가 커널 가상 메모리 범위 내에 있거나, 읽기 전용 페이지에 쓰기를 시도하는 경우, 이 접근은 유효하지 않습니다. 모든 유효하지 않은 접근은 프로세스를 종료시키고 그 자원을 모두 해제합니다.

// 	2.	페이지를 저장할 프레임 확보하기
// 공유를 구현한 경우, 필요한 데이터가 이미 프레임에 있을 수 있으므로 그 프레임을 찾을 수 있어야 합니다.

// 	3.	프레임에 데이터를 가져오기
// 데이터를 파일 시스템 또는 스왑에서 읽어오거나, 데이터를 0으로 채우는 등의 방법으로 프레임에 데이터를 가져옵니다. 공유를 구현한 경우,
// 필요한 페이지가 이미 프레임에 있을 수도 있으므로 이 단계에서는 아무 작업도 필요하지 않을 수 있습니다.

// 	4.	폴팅된 가상 주소에 대해 페이지 테이블 항목을 물리적 페이지로 설정하기
// threads/mmu.c에 있는 함수들을 사용하여 이를 설정합니다.

/* 성공 시 true를 반환합니다. */

// - 페이지 폴트가 발생하면 `userprog/exception.c`의 `page_fault()`에서 `vm_try_handle_fault` 함수를 호출합니다.
// - 이 함수에서 **해당 페이지 폴트가 스택 확장으로 처리 가능한지**를 판단해야 합니다.
// - 판단 기준을 만족하면 `vm_stack_growth()`를 호출해 해당 주소까지 스택을 확장합니다.

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

	// HACK: not_present 처리는 언제 해 주는지 잘 모르겠음.
	dprintfc("[vm_try_handle_fault] fault handle start. addr: %p\n", addr);
	struct supplemental_page_table *spt = &thread_current()->spt; // 현재 쓰레드의 spt 가져옴.

	
	if (not_present) 
	{
		void *rsp = is_kernel_vaddr(f->rsp) ? thread_current()->rsp : f->rsp;
	
		/* DEBUG: 기존 스택 성장 조건은 아래와 같았음.
		 * `if (f->rsp - 8 == addr)`
		 * 차이점: f->rsp가 페이지 폴트 발생 위치보다 위에 있을 경우를 고려하지 않았음.
		 */
	
		if (addr >= rsp - 8 && addr < USER_STACK && addr >= STACK_MAX) // 합법적인 스택 확장 요청인지 판단. user stack의 최대 크기인 1MB를 초과하지 않는지 check
		{
			dprintff("[vm_try_handle_fault] expending stack page\n");
			vm_stack_growth(pg_round_down(addr));
			return true;
		}
		else
		{
			struct page *page;
			page = spt_find_page(spt, addr); // page를 null로 설정해. stack growth 경우에는 spt 찾을 필요 없지 않나? 어차피 없을텐데.
			if (write && !page->writable)
			{
        		return false;
			}
			return vm_do_claim_page(page);	 // 그 페이지에 대응하는 프레임을 할당받아.
		}
	}
	else
	{
		return false;
	}
}

stack growth() 에서 문제가 발생

	/* DEBUG: 기존 스택 성장 조건은 아래와 같았음.
	 * `if (f->rsp - 8 == addr)`
	 * 차이점: f->rsp가 페이지 폴트 발생 위치보다 위에 있을 경우를 고려하지 않았음.
	 */

	if (addr >= rsp - 8 && addr < USER_STACK && addr >= STACK_MAX)

log

[vm_do_claim_page] routine start. page->va: 0x4747f000
[vm_do_claim_page] do claim success. va: 0x4747f000, pa: 0x8004a21000
[vm_try_handle_fault] fault handle start. addr: 0x400f68
[vm_try_handle_fault] checking f->rsp: 0x4747ffd8
[vm_do_claim_page] routine start. page->va: 0x400000
[vm_do_claim_page] do claim success. va: 0x400000, pa: 0x8004a22000
[page_fault] vm_try_handle_fault 성공
[vm_try_handle_fault] fault handle start. addr: 0x605ce8
[vm_try_handle_fault] checking f->rsp: 0x4747ffa0
[vm_do_claim_page] routine start. page->va: 0x605000
[vm_do_claim_page] do claim success. va: 0x605000, pa: 0x8004a23000
[page_fault] vm_try_handle_fault 성공
[vm_try_handle_fault] fault handle start. addr: 0x40135f
[vm_try_handle_fault] checking f->rsp: 0x4747fee8
[vm_do_claim_page] routine start. page->va: 0x401000
[vm_do_claim_page] do claim success. va: 0x401000, pa: 0x8004a24000
[page_fault] vm_try_handle_fault 성공
[vm_try_handle_fault] fault handle start. addr: 0x4046a8
[vm_try_handle_fault] checking f->rsp: 0x4747fdc0
[vm_do_claim_page] routine start. page->va: 0x404000
[vm_do_claim_page] do claim success. va: 0x404000, pa: 0x8004a25000
[page_fault] vm_try_handle_fault 성공
[vm_try_handle_fault] fault handle start. addr: 0x402000
[vm_try_handle_fault] checking f->rsp: 0x4747fd98
[vm_do_claim_page] routine start. page->va: 0x402000
[vm_do_claim_page] do claim success. va: 0x402000, pa: 0x8004a26000
[page_fault] vm_try_handle_fault 성공
[vm_try_handle_fault] fault handle start. addr: 0x403157
[vm_try_handle_fault] checking f->rsp: 0x4747fdb8
[vm_do_claim_page] routine start. page->va: 0x403000
[vm_do_claim_page] do claim success. va: 0x403000, pa: 0x8004a27000
[page_fault] vm_try_handle_fault 성공
(pt-big-stk-obj) begin
[vm_try_handle_fault] fault handle start. addr: 0x4746fe78
[vm_try_handle_fault] checking f->rsp: 0x4746fe80
[vm_try_handle_fault] expending stack page
[vm_stack_growth] routine start
[vm_stack_growth] vm_alloc complete. result: 1
[vm_do_claim_page] routine start. page->va: 0x4746f000
[vm_do_claim_page] do claim success. va: 0x4746f000, pa: 0x8004a28000
[page_fault] vm_try_handle_fault 성공
[vm_try_handle_fault] fault handle start. addr: 0x47470000
[vm_try_handle_fault] checking f->rsp: 0x4746fe40
pt-big-stk-obj: exit(-1)
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4746fe78
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47470000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47471000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47472000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47473000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47474000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47475000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47476000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47477000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47478000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47479000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747a000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747b000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747c000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747d000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747e000

align하고 다시 출력

(pt-big-stk-obj) begin
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4746f000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47470000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47471000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47472000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47473000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47474000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47475000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47476000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47477000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47478000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x47479000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747a000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747b000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747c000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747d000
[vm_stack_growth] vm_alloc complete. result: 1. stack address: 0x4747e000

여기서 고민한 문제는 user stack은 위에서 아래로 성장하기 때문에 주소값이 줄어들어야 된다고 생각했다. 하지만 계속 증가하고 있는 상황. 이 상황이 이해가 되질 않았다.

이 부분에서 한참 고민했다. if 조건문을 확인해보면 rsp에서 8byte 아래 위치를 참조한다면 page fault()가 발생할 것이기 때문에 stack_growth()로 stack을 확장시킨다.

당연히 page_fault()가 발생하는 시점이 이 경우 하나 밖에 없다고 생각하고, 해결책을 찾지 못했다. 하지만 rsp가 한 page(4KB)씩 이동하는 것이 아니라 한 번에 여러 page만큼 이동할 경우 rsp 보다 높은 위치에서 page fault()가 발생할 수 있지 않을까? 라는 생각을 했다.

그 결과로 > 부등호를 추가했고, test case를 통과했다.

addr < USER_STACK && addr >= STACK_MAX

이 부분은 user stack 영역을 지정한 부분이다.

vm_stack_growth() 구현 (vm/vm.c)

/* 스택 확장 */
// - 주어진 주소 `addr`까지 스택 크기를 확장합니다.
// - 하나 이상의 **익명 페이지(anonymous page)**를 할당해 폴트가 더 이상 발생하지 않도록 합니다. // HACK: "하나 이상?"
// - 이때 **`addr`은 반드시 페이지 단위(PGSIZE)로 내림(round down)** 처리 후 할당해야 합니다.
// - 대부분의 운영체제는 **스택의 최대 크기를 제한**합니다.
// - 예: 유닉스 계열의 `ulimit` 명령으로 조정 가능
// - GNU/Linux 시스템의 기본값: 약 **8MB**
// - 본 프로젝트에서는 스택 최대 크기를 **1MB**로 제한해야 합니다. // HACK: 제한 안 걸어 놨음.
static void
vm_stack_growth(void *addr)
{
	dprintff("[vm_stack_growth] routine start\n");
	bool result = vm_alloc_page_with_initializer(VM_ANON | VM_MARKER_STACK, addr, true, NULL, NULL);
	vm_claim_page(addr);
	dprintff("[vm_stack_growth] vm_alloc complete. result: %d. stack address: %p\n", result, addr);
}

결과

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
pass tests/userprog/fork-read
pass tests/userprog/fork-close
pass tests/userprog/fork-boundary
pass tests/userprog/exec-once
pass tests/userprog/exec-arg
pass tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
pass 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
pass tests/vm/pt-write-code2
FAIL tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
pass 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
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass 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
pass 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
pass tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
pass 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
24 of 141 tests failed.
profile
취업 준비생 낚곰입니다!! 반갑습니다!!

0개의 댓글