크래프톤 정글 TIL : 0928

lazyArtisan·2024년 9월 28일
0

정글 TIL

목록 보기
90/147

📝 배운 것들


VScode에서 상위 폴더가 있으면 F12로 함수 들어갈 때 다른 곳으로 들어가버림



🖥️ PintOS

🔷 Virtual Memory


setup_stack()을 하고 있었고
그걸 위해 vm_claim_page()을 재점검 하고 있었음

vm_claim_page() 재점검

va로 해시 테이블(spt)을 탐색하여 내가 넣었던
uninit 페이지를 찾고, 그걸 page 변수에 담은 후에
vm_do_claim_page에 인자로 넘겨주면 될듯.

그리고 그건 내일의 내가.

hash.c에는 find 관련 함수가 3가지 있다.

	struct list *bucket = find_bucket(h, new);
	struct hash_elem *old = find_elem(h, bucket, new);

사용 예시를 보니 find_bucket은 list를 반환하고,
find_elem은 hash_elem을 반환한다.

/* e와 같은 원소를 해시 테이블에서 찾아서 반환한다. 없으면 NULL 반환. */
struct hash_elem *
hash_find(struct hash *h, struct hash_elem *e)
{
	return find_elem(h, find_bucket(h, e), e);
}

hash_find는 find_elem과 find_bucket을 둘 다 사용한다.

hash_elemthread_current()은 그냥 list_elem이다.
각 element에 대해 고유한 값.

주소로 값을 찾으려면 어떻게 해야되지?

static struct hash_elem *
find_elem(struct hash *h, struct list *bucket, struct hash_elem *e)
{
	struct list_elem *i;

	for (i = list_begin(bucket); i != list_end(bucket); i = list_next(i))
	{
		struct hash_elem *hi = list_elem_to_hash_elem(i);
		if (!h->less(hi, e, h->aux) && !h->less(e, hi, h->aux))
			return hi;
	}
	return NULL;
}

find_elem은 버킷에서 한 번 더 들어가서 원소를 찾는 느낌
find_bucket은 그렇다면 해쉬 함수의 결과값이 같은 걸 찾는다는 건데

/* Returns the bucket in H that E belongs in. */
static struct list *
find_bucket(struct hash *h, struct hash_elem *e)
{
	size_t bucket_idx = h->hash(e, h->aux) & (h->bucket_cnt - 1);
	return &h->buckets[bucket_idx];
}

elem을 어떻게 가공해서 va가 같으면 같은 원소로 취급되도록 하는걸까?
h->hash(e, h->aux) 이 부분이 신경쓰임.

내가 넣어줬던 hash 함수는 page_hash.
따라가보니 hash_entry가 핵심인듯.

/* hash_elem의 포인터를 hash_elem이 들어가있는 구조체의 포인터로 변환한다.
 * 구조체 struct의 이름과 hash_elem의 멤버 이름 member를 제공해라.
 * 예시로 파일 맨 위에 있는 큰 주석 봐라. */

hash_entry의 주석.

/* 해시에 포함될 수 있는 각 구조체는 struct hash_elem 멤버를 포함해야 합니다.
 * 모든 해시 함수는 이 struct hash_elem을 대상으로 작동합니다.
 * hash_entry 매크로를 사용하면 struct hash_elem에서 다시
 * 그것을 포함하는 구조체 객체로 변환할 수 있습니다. */

파일 맨 위에 있는 큰 주석.

계속 읽는데도 뭐 어떻게 돌아가는건지 정확하게 이해가 안된다.

일단 find_bucket에서 hash 뭐시기가 있었으니까
hash_find가 나머지 두 개 다 쓰니까
일단 이거 쓰는게 맞을것이라 추측하고 써보기. 안되면 다른거 써보자.

/* Claim the page that allocate on VA. */
bool vm_claim_page(void *va UNUSED)
{
	struct page *page;
	page->addr = va; // 빈 페이지에 찾으려는 페이지의 가상 주소 넣고
	struct hash_elem *hash_e = hash_find(&thread_current()->spt.pages, &page->hash_elem);
	// hash_find로 해당 가상 주소를 가진 페이지의 hash_elem을 찾아서
	page = hash_entry(hash_e, struct page, hash_elem); // 페이지로 다시 변환하기
	return vm_do_claim_page(page);					   // 찾은 페이지 claim
}

해치웠나?

setup_stack()으로 다시 돌아가자.

setup_stack()

당신은 스택을 확인하는 방법을 제공해야 합니다. 당신은 vm/vm.h의 vm_type에 있는 보조 marker(예 - VM_MARKER_0)들을 페이지를 마킹하는데 사용할 수 있습니다.

깃북을 다시 보니 이런 얘기가 있었음.

대충 이런 식으로 쓰는 거라고 함.

일단 나중에 필요하면 더 추가하면 되고, 당장 필요한 정보는
스택인지 아닌지만 필요한 거니까 VM_MARKER_0를 '스택인지 아닌지'로 표시하기로 함.

/* Create a PAGE of stack at the USER_STACK. Return true on success. */
/* USER_STACK에 페이지만큼의 스택을 만든다. 성공하면 true 반환. */
static bool
setup_stack(struct intr_frame *if_)
{
	bool success = false;
	void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE);

	/* TODO: Map the stack on stack_bottom and claim the page immediately.
	 * TODO: If success, set the rsp accordingly.
	 * TODO: You should mark the page is stack. */
	/* TODO: 스택을 `stack_bottom`에 매핑하고 페이지를 즉시 클레임해라.
	 * TODO: 성공하면, rsp(스택 포인터)를 적절히 설정해라.
	 * TODO: 해당 페이지가 스택에 있다고 표시해라.*/

	if (vm_alloc_page(VM_UNINIT | VM_MARKER_0, stack_bottom, true) && vm_claim_page(stack_bottom))
		success = false;
	if_->rsp = USER_STACK;
	struct page *page = spt_find_page(&thread_current()->spt, stack_bottom);

	return success;
}

결국 동기에게 헬프콜 쳐서 힌트랑 설명 얻음.
스택에 있는지 표시하는 건 나중에 해야할듯.
vm_alloc_page에 spt 삽입도 포함돼있음.

vm_try_handle_fault()

최종적으로 spt_find_page 를 거쳐 보조 페이지 테이블을 참고하여 fault된 주소에 대응하는 페이지 구조체를 해결하기 위한 함수 vm_try_handle_fault를 수정하세요.

/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page(struct supplemental_page_table *spt UNUSED, void *va UNUSED)
{
	struct page *page = NULL;
	/* TODO: Fill this function. */
	page = hash_find(&spt->pages, va);
	return page;

	struct page *page;
	page->addr = va;
	page = hash_find(&spt->pages, page->hash_elem);
	return page;
}

spt_find_page를 먼저 고치는데 hash_find가 이상하게 되어있는거 발견.
그러다가 va랑 addr 두 개가 있다는거 발견. 아니 도대체 이건 뭐임

addr은 내가 만든건데
va에 그냥 넣으면 됐던 건가?

addr 넣었던 이유를 다시 보니까 hash 테이블 비교 함수에 필요했어서 그랬던 거임
이건 문제 아닌듯

/* Insert PAGE into spt with validation. */
bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
					 struct page *page UNUSED)
{
	int succ = false;
	/* TODO: Fill this function. */
	if (!spt_find_page(spt, page))
		return false;

	if (hash_insert(spt->hash, &page->hash_elem))
		succ = true;
	return succ;
}

이건 또 왜 이렇게 돼있는거임
spt_find_page(spt, page) 라고?
인자로 들어온 va의 정체가 struct page *라고?

원래 맞는건가?

/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page(struct supplemental_page_table *spt UNUSED, void *va UNUSED)
{
	struct page *page;
	page->addr = va;
	page = hash_find(&spt->pages, &page->hash_elem);
	return page;
}

일단 spt_find_page 고쳤음

...

동기가 붙어서 이것저것 알려주고 뜯어고쳐줌
컴파일이 돌긴 하는데 바로 패닉뜸

뭔가 빠진게 있거나 틀린게 있거나 해서 그렇다고 함
처음부터 다 다시 점검

처음부터 다 다시 점검

남은 시간이 촉박하기 때문에 어쩔 수 없이 답지 보며 비교

https://e-juhee.tistory.com/entry/Pintos-KAIST-Project-3-Memory-Management

spt_find_page

/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page(struct supplemental_page_table *spt UNUSED, void *va UNUSED)
{
	struct page *page;
	page->va = va;
	page = hash_find(&spt->pages, &page->hash_elem);
	return page;
}

이전

/* Find VA from spt and return page. On error, return NULL. */
struct page *
spt_find_page(struct supplemental_page_table *spt UNUSED, void *va UNUSED)
{
	struct page *page;
	page->va = va;
	struct hash_elem *e = hash_find(&spt->pages, &page->hash_elem);
	return e != NULL ? hash_entry(e, struct page, hash_elem) : NULL;
}

이후

hash 비교 함수도 addr에서 va로 바꿔줌

spt_insert_page

/* Insert PAGE into spt with validation. */
bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
					 struct page *page UNUSED)
{
	int succ = false;
	/* TODO: Fill this function. */
	if (!spt_find_page(spt, page))
		return false;

	if (hash_insert(&spt->pages, &page->hash_elem))
		succ = true;
	return succ;
}

이전

/* Insert PAGE into spt with validation. */
bool spt_insert_page(struct supplemental_page_table *spt UNUSED,
					 struct page *page UNUSED)
{
	return hash_insert(&spt, &page->hash_elem) == NULL ? true : false; // 존재하지 않을 경우에만 삽입
}

이후

굳이 spt_find_page를 해줄 필요 없었다

vm_do_claim_page

/* Claim the PAGE and set up the mmu. */
static bool
vm_do_claim_page(struct page *page)
{
	struct frame *frame = vm_get_frame();

	/* Set links */
	frame->page = page;
	page->frame = frame;

	/* TODO: Insert page table entry to map page's VA to frame's PA. */
	/* 페이지 테이블 엔트리를 삽입해서 가상 주소를 프레임의 물리 주소로 매핑해라 */
	if (!pml4_set_page(thread_current()->pl4, page->va, frame->kva, page->writable))
		return NULL;

	return swap_in(page, frame->kva);
}

pml4인데 pl4라고 적혀있었음.

vm_claim_page

/* Claim the page that allocate on VA. */
bool vm_claim_page(void *va UNUSED)
{
	struct page *page;
	page->addr = va; // 빈 페이지에 찾으려는 페이지의 가상 주소 넣고
	struct hash_elem *hash_e = hash_find(&thread_current()->spt.pages, &page->hash_elem);
	// hash_find로 해당 가상 주소를 가진 페이지의 hash_elem을 찾아서
	page = hash_entry(hash_e, struct page, hash_elem); // 페이지로 다시 변환하기
	return vm_do_claim_page(page);					   // 찾은 페이지 claim
}

이전

/* Claim the page that allocate on VA. */
bool vm_claim_page(void *va UNUSED)
{
	struct page *page;
	page->addr = va; // 빈 페이지에 찾으려는 페이지의 가상 주소 넣고
	page = spt_find_page(&thread_current()->spt, va);
	if (page == NULL)
		return false;
	return vm_do_claim_page(page); // 찾은 페이지 claim
}

이후

spt_find_page가 있었는데 hash_find로 힘들게 할 필요 없었다.

vm_alloc_page_with_initializer

bool vm_alloc_page_with_initializer(enum vm_type type, void *upage, bool writable,
									vm_initializer *init, void *aux)
{
	ASSERT(VM_TYPE(type) != VM_UNINIT)

	struct supplemental_page_table *spt = &thread_current()->spt;

	/* Check wheter the upage is already occupied or not. */
	if (spt_find_page(spt, upage) == NULL)
	{
		/* TODO: Create the page, fetch the initialier according to the VM type,
		 * TODO: and then create "uninit" page struct by calling uninit_new. You
		 * TODO: should modify the field after calling the uninit_new. */
		/* 페이지를 만들고, 가상 메모리 타입에 따라 initialier를 가져오고,
		 * uninit_new를 호출해서 "초기화 안된" 페이지를 생성한다.
		 * uninit_new를 호출한 후에 field를 수정해야 한다. */



<br>
<br>

# ⚔️ 백준
---

## 📌 
		struct page *page = (struct page *)malloc(sizeof(struct page));

		bool (*initializer)(struct page *, enum vm_type, void *kva);
		if (type == VM_ANON)
			initializer = &anon_initializer;
		if (type == VM_FILE)
			initializer = &file_backed_initializer;

		uninit_new(page, upage, init, type, aux, initializer);
		page->writable = writable;

		/* TODO: Insert the page into the spt. */
		hash_insert(&spt->pages, &page->hash_elem);
	}
err:
	return false;
}

이전

		/* TODO: Insert the page into the spt. */
		return spt_insert_page(spt, page);

이후

역시 비슷하게 spt_insert_page가 있었는데 hash_insert로 해버림.
return도 안 했었음.

lazy_load_segment

static bool lazy_load_segment(struct page *page, void *aux)
{
	struct aux_pak *aux_pak = (struct aux_pak *)aux;
	struct file *file = aux_pak->file;
	off_t ofs = aux_pak->ofs;
	uint8_t upage = aux_pak->upage;
	uint32_t read_bytes = aux_pak->read_bytes;
	uint32_t zero_bytes = aux_pak->zero_bytes;
	bool writable = aux_pak->writable;

	/* TODO: Load the segment from the file */
	/* TODO: This called when the first page fault occurs on address VA. */
	/* TODO: VA is available when calling this function. */
	/* 할것: 파일에서 세그먼트를 load한다 */
	/* 할것: 이 함수는 주소 VA에서 첫번째 페이지 폴트가 일어날 때 호출된다. */
	/* 할것: VA는 이 함수가 호출될 때 사용 가능하다 */

	file_seek(file, ofs); // 오프셋에 있는 파일을 찾는다
	while (read_bytes > 0 || zero_bytes > 0)
	{
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
		// 읽을만큼의 바이트가 페이지 사이즈보다 작으면 페이지 사이즈로 만들어준다.
		size_t page_zero_bytes = PGSIZE - page_read_bytes;
		// 페이지 단위 정렬을 맞춰주기 위해 남는 만큼 0으로 초기화해줄 바이트 크기

		/* 페이지만큼의 메모리를 얻는다 */
		uint8_t *kpage = palloc_get_page(PAL_USER);
		if (kpage == NULL)
			return false;

		/* 페이지를 로드한다. (파일을 읽는다) */
		if (file_read(file, kpage, page_read_bytes) != (int)page_read_bytes)
		{
			palloc_free_page(kpage);
			return false;
		}
		memset(kpage + page_read_bytes, 0, page_zero_bytes);

		/* 프로세스의 주소 공간에 그 페이지를 추가한다. */
		struct thread *t = thread_current();
		if (!pml4_get_page(t->pml4, upage) == NULL && pml4_set_page(t->pml4, upage, kpage, writable))
		{
			printf("fail\n");
			palloc_free_page(kpage);
			return false;
		}
	}
	return true;
}

이전

static bool lazy_load_segment(struct page *page, void *aux)
{
	struct aux_pak *aux_pak = (struct aux_pak *)aux;
	struct file *file = aux_pak->file;
	off_t ofs = aux_pak->ofs;
	uint8_t upage = aux_pak->upage;
	uint32_t read_bytes = aux_pak->read_bytes;
	uint32_t zero_bytes = aux_pak->zero_bytes;
	bool writable = aux_pak->writable;

	/* TODO: Load the segment from the file */
	/* TODO: This called when the first page fault occurs on address VA. */
	/* TODO: VA is available when calling this function. */
	/* 할것: 파일에서 세그먼트를 load한다 */
	/* 할것: 이 함수는 주소 VA에서 첫번째 페이지 폴트가 일어날 때 호출된다. */
	/* 할것: VA는 이 함수가 호출될 때 사용 가능하다 */

	file_seek(file, ofs); // ofs부터 읽겠다

	/* 페이지를 로드한다. (파일을 읽는다) */
	if (file_read(file, page->frame->kva, read_bytes) != (int)read_bytes)
	{
		palloc_free_page(page->frame->kva);
		return false;
	}

	return true;
}

이후

필요없는 부분 다수 제거 (Project 2 이전 코드를 무분별하게 참고해버린듯)
load_segment에 있던 aux_pak->read_bytes를 page_read_bytes로도 바꿔줌

vm_try_handle_fault()

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

	if ((page = spt_find_page(spt, addr)) == NULL)
		return false;

	return vm_do_claim_page(page);
}

이전

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

	if (not_present) // 접근한 메모리의 physical page가 존재하지 않은 경우
	{
		/* TODO: Validate the fault */
		page = spt_find_page(spt, addr);
		if (page == NULL)
			return false;
		if (write == 1 && page->writable == 0) // write 불가능한 페이지에 write 요청한 경우
			return false;
		return vm_do_claim_page(page);
	}
	return false;
}

이후

hread_current(): assertion is_thread (t) failed.

다 고쳐주고 나니 뭔가 오류가 길게 뜨기 시작함

오류의 원인은

Kernel panic in run: PANIC at ../../threads/thread.c:365 in thread_current(): assertion is_thread (t)' failed.

동기 두 명이 한참 붙어서 이것저것 고쳐줬는데 원인조차 찾지 못함.
(자기 문제도 아닌데 도와준 두 명에게 감사함...)

결국 테세우스의 배 전략을 사용하기로 함.
(= 잘 돌아가는 코드에서 함수 하나씩 바꿔보기)

vm_alloc_page_with_initializer 바꿔봤더니 바로 반응이 옴

0x0000008004218772: debug_panic (lib/kernel/debug.c:32)
0x00000080042076a3: thread_current (threads/thread.c:366)
0x000000800420ada7: lock_held_by_current_thread (threads/synch.c:287)
0x000000800421bb51: console_locked_by_current_thread (lib/kernel/console.c:111 (discriminator 2))
0x000000800421bd1e: putchar_have_lock (lib/kernel/console.c:173)
0x000000800421bd02: vprintf_helper (lib/kernel/console.c:166)
0x0000008004215cbe: __vprintf (lib/stdio.c:154)
0x000000800421bbb1: vprintf (lib/kernel/console.c:123)
0x0000008004215c75: printf (lib/stdio.c:80)
0x0000008004221ac3: vm_try_handle_fault (vm/vm.c:228)
0x000000800421d76c: page_fault (userprog/exception.c:145)
0x00000080042094f1: intr_handler (threads/interrupt.c:352)
0x000000800420990f: intr_entry (threads/intr-stubs.o:?)
0x0000008004221e5b: uninit_initialize (vm/uninit.c:60)
0x0000008004221cbc: vm_do_claim_page (vm/vm.c:280)
0x0000008004221bfa: vm_claim_page (vm/vm.c:261)
0x000000800421d2b5: setup_stack (userprog/process.c:966)
0x000000800421cb5a: load (userprog/process.c:639)
0x000000800421c378: process_exec (userprog/process.c:291)
0x000000800421bec9: initd (userprog/process.c:94)
0x0000008004207ca2: kernel_thread (threads/thread.c:537)

이전 백트레이스

0x0000008004218772: debug_panic (lib/kernel/debug.c:32)
0x000000800421d675: kill (userprog/exception.c:103)
0x00000080042094f1: intr_handler (threads/interrupt.c:352)
0x000000800420990f: intr_entry (threads/intr-stubs.o:?)
0x0000008004221b54: vm_try_handle_fault (vm/vm.c:234)
0x000000800421d76c: page_fault (userprog/exception.c:145)
0x00000080042094f1: intr_handler (threads/interrupt.c:352)
0x000000800420990f: intr_entry (threads/intr-stubs.o:?)

답지 백트레이스

허리 아파서 오늘은 여기까지

0개의 댓글