VScode에서 상위 폴더가 있으면 F12로 함수 들어갈 때 다른 곳으로 들어가버림
setup_stack()을 하고 있었고
그걸 위해 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()으로 다시 돌아가자.
당신은 스택을 확인하는 방법을 제공해야 합니다. 당신은 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 삽입도 포함돼있음.
최종적으로 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
/* 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로 바꿔줌
/* 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를 해줄 필요 없었다
/* 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라고 적혀있었음.
/* 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로 힘들게 할 필요 없었다.
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도 안 했었음.
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로도 바꿔줌
/* 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;
}
이후
다 고쳐주고 나니 뭔가 오류가 길게 뜨기 시작함
오류의 원인은
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:?)
답지 백트레이스
허리 아파서 오늘은 여기까지