회고
오늘은 Pint OS 프로젝트 3의 마지막 날이다. 모든 과제를 끝내지는 못했지만, fail의 수를 5개까지 줄일 수 있었다. 중간에 에러 처리에서 헛발질만 안 했어도 시간이 더 있었을텐데 아쉽다. Swapping이 마지막인 줄 알았는데, Swapping을 구현해도 5개의 테스트 케이스가 남는다. 동기화 관련 문제라고 하던데 여기까진 손을 못 대겠다.. 처음 터졌을 때부터 고쳤으면 수월했겠지만, 코드를 너무 많이 고친 지금은 손을 댈 엄두가 나지 않는다.
다음주 일주일 간은 프로젝트 4 Extra를 진행한다. 말 그대로 Extra라서 구현하는 것은 옵션사항이다. 결국 프로젝트 3가 가장 힘든 주간이 아니었나 싶다.
프로젝트 3의 주 내용은 Virtual Memory이다. 물리 메모리의 용량 한계로 실행할 수 없는 프로그램을 실행할 수 있게 해주거나 멀티태스킹 환경에서 각 프로세스마다 독립된 가상 주소 공간을 가지며 실제 물리 메모리에 매핑할 수 있게 해준다. 프로젝트 2까지는 물리 메모리에 매핑되지 않은 가상 주소에 접근을 시도하면 Page Fault가 발생하며 그대로 죽어버렸다. 프로젝트 3에서는 Page Fault가 발생한 시점부터 해당 가상 주소에 해당하는 데이터를 파일로부터 Lazy loading 하여 물리 메모리에 매핑한다. 필요한 데이터가 현재 물리 메모리에 존재하지 않더라도 언제든지 load하여 꺼낼 수 있다. 이를 통해 무한한 메모리 공간의 환상을 보여준다.
이를 구현하기 위해서는 가상 메모리의 이해가 필수적이다. 리눅스와 달리 핀토스의 메모리 공간은 특이한데, 아무래도 실제 물리 메모리에 구현하는 것이 아니다 보니, 가상 메모리 위에 물리 메모리를 얹어서 하나의 메모리로 관리되도록 해놓았다. 깃북에서 친절하게 설명을 해주지는 않아서 코드를 뜯어보면서 작동 방식을 이해하는 수 밖에 없었다.
2주 간 어려웠던 점
프로젝트 3는 2주 동안 진행되었다. 프로젝트 3에서 어려웠던 점은 역시 VM에 대한 이해와 코드 로직이었다. 함수 호출 구조가 복잡하게 얽혀있어서 하나를 구현하다 가도 이게 어디서 호출돼서 작동하는건지 종종 잊어버린다. 그리고 그냥 할 게 많았다. Memory Management부터 Anonymous Page, Stack Growth, Memory Mapped Files, Swap In/Out까지... 생각보다는 할 만 했지만 중간에 트러블 슈팅 이슈가 너무 많았다. 테스트 케이스를 하나하나 뜯어보고 printf를 찍어보면서 터진 곳을 추적하고 수정하다가 다른 곳이 터지고.. 이런 일의 반복이었다.
프로젝트 3가 끝나갈 때쯤에는 방전되었다. 2주 동안 거의 같은 것만 하다보니 질리기도 해서 의욕이 확 꺾였다. 그래도 열심히 하는 동료들을 보면서 의욕이 생겼다.
향후
일주일 간의 프로젝트 4가 끝나면 드디어 "나만의 무기" 프로젝트이다. 크래프톤 정글은 SW사관학교 정글 과정을 그대로 가져왔기에 어떤 프로젝트인지, 어떻게 진행되는지는 대강 알고 있다. 남은 50여일 동안 거대한 프로젝트를 수행하며 자신만의 무기를 만든다. 정글에서의 80일 간의 여정은 이때를 위한 것이다. 얼마나 재밌을지 벌써 기대가 된다.
트러블 슈팅
munmap을 구현하고, 스레드가 자꾸 두 번 죽는 이슈가 있었다. munmap으로 파일 매핑을 해제하면서 보조 페이지 테이블과 페이지 테이블에서 삭제하는데, 이때 destroy() 함수가 호출된다.
static void
file_backed_destroy (struct page *page) {
struct file_page *file_page = &page->file;
struct file *file = file_page->file;
struct thread *t = thread_current ();
void *kpage = page->frame->kva; // 여기서 page->frame이 NULL일 때, 터짐.
off_t size = file_page->read_bytes;
off_t start = file_page->ofs;
ASSERT (page != NULL);
if (page->writable && pml4_is_dirty (t->pml4, page->va)) {
file_write_at (file, kpage, size, start);
pml4_set_dirty (t->pml4, page->va, false);
}
hash_delete (&t->spt.spt_hash, &page->h_elem);
pml4_clear_page (t->pml4, page->va);
}
Lazy loading되었다가 Swap Out된 VM_FIEL 타입의 페이지를 삭제할 때 file_backed_destroy() 함수가 호출되는데 page의 존재하지 않는 frame의 값(NULL)을 참조하면서 다시 한 번 Page Fault가 발생한다. 이 때 Pint OS는 NULL을 Lazy loading을 시도하면서 죽게 된다(exit(-1).
exit()으로 죽은 프로세스는 process_exit() => process_cleanup() => supplemental_page_table_kill()을 호출하면서 아까 그 페이지를 다시 destroy()를 시도하면서 또 죽는다. 이때는 무한루프에 걸리지 않고 레지스터 값이 꼬이면서 그대로 죽는다.
static void
file_backed_destroy (struct page *page) {
struct file_page *file_page = &page->file;
struct file *file = file_page->file;
struct thread *t = thread_current ();
if (page->frame == NULL) {
hash_delete (&t->spt.spt_hash, &page->h_elem);
pml4_clear_page (t->pml4, page->va);
return;
}
void *kpage = page->frame->kva;
off_t size = file_page->read_bytes;
off_t start = file_page->ofs;
ASSERT (page != NULL);
if (page->writable && pml4_is_dirty (t->pml4, page->va)) {
file_write_at (file, kpage, size, start);
pml4_set_dirty (t->pml4, page->va, false);
}
hash_delete (&t->spt.spt_hash, &page->h_elem);
pml4_clear_page (t->pml4, page->va);
}
'프로젝트 > Pint OS' 카테고리의 다른 글
[Pint OS] Stack Growth (0) | 2023.06.25 |
---|---|
[Pint OS] Lazy loading(3) (0) | 2023.06.22 |
[Pint OS] Lazy loading(2) (0) | 2023.06.19 |
[Pint OS] Lazy loading(1) (0) | 2023.06.18 |
[Pint OS] System Calls (6) (0) | 2023.06.17 |