Krafton Jungle/5. PintOS

[PintOS 5주차] Day 4-5

munsik22 2025. 6. 9. 11:35

메롱하는 문식이


Swap in/out 구현 이어하기

File backed page의 swap in/out을 구현한 뒤에, failed tests의 수가 16개로 늘어나는 문제가 발생했다.

Oops!

static bool
file_backed_swap_out (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;

	if (pml4_is_dirty(thread_current()->pml4,page->va)){
		file_write_at(file_page->file, page->frame->kva, file_page->read_bytes, file_page->ofs);
		pml4_set_dirty(thread_current()->pml4, page->va, false);
	} 
	pml4_clear_page(thread_current()->pml4, page->va);
	list_remove(&page->frame->frame_elem);
	// palloc_free_page(page->frame->kva);
	// free(page->frame);
	page->frame = NULL;
	return true;
}
static void
file_backed_destroy (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;
	if (file_page->file == NULL)
		return;
    if (pml4_is_dirty(thread_current()->pml4, page->va)) {
        file_write_at(file_page->file, page->frame->kva, file_page->read_bytes, file_page->ofs);
        pml4_set_dirty(thread_current()->pml4, page->va, false);
    }
    pml4_clear_page(thread_current()->pml4, page->va);
	list_remove(&page->frame->frame_elem);
	// palloc_free_page(page->frame->kva);
	// free(page->frame);
	// page->frame = NULL;
}
  • 위의 함수들에서 kva 및 frame을 free하는 함수들을 삭제(주석 처리)했더니 해결되었다.
  • 16 → 9 of 141 tests failed.

Anon page의 swap in/out을 구현한 뒤에, 이전에 pass했던 pt-grow-stack 등의 테스트가 fail이 나는 문제가 발생했다.

backtrace result

"This assertion commonly fails when accessing a file via an inode that has been closed and freed. Freeing an inode clears all its sector indexes to 0xcccccccc, which is not a valid sector number for disks smaller than about 1.6 TB."

 

위 에러 메시지가 설명하듯, 이미 close되고 해제된 inode를 통해 파일 접근이 시도되었을 가능성이 높은 것으로 보인다.

static bool
vm_do_claim_page (struct page *page) {
	struct frame *frame = vm_get_frame ();
	if (frame == NULL)
		return false;

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

	/* TODO: Insert page table entry to map page's VA to frame's PA. */
	/* Add the mapping from the VA to the PA in the page table. */
	if (!pml4_set_page(thread_current()->pml4, page->va, frame->kva, page->writable)) {
		printf("[debug] pml4_set_page failed at %p\n", page->va);
		return false;
	}

	if (!swap_in(page, frame->kva)) {
		printf("[debug] swap_in failed at %p\n", page->va);
		return false;
	}

    return true;
}

디버깅 결과 swap in이 제대로 동작하지 않았음을 발견했다.

static bool
anon_swap_in (struct page *page, void *kva) {
	struct anon_page *anon_page = &page->anon;
	size_t slot_idx = anon_page->slot_idx;
	if (slot_idx == BITMAP_ERROR) {
		return false
	}

    …
}
  • 기존에 구현했던 anon_swap_in()에서는 slot_idx == BITMAP_ERROR인 경우 단순히 false를 리턴했다.
  • 하지만 stack growth 같은 경우는 swap out된 적 없는 "처음 생성된" 페이지이므로 swap_in()이 아무것도 로딩하지 않아도 정상적으로 동작해야 한다.
static bool
anon_swap_in (struct page *page, void *kva) {
	struct anon_page *anon_page = &page->anon;
	size_t slot_idx = anon_page->slot_idx;
	if (slot_idx == BITMAP_ERROR) {
		// return false; ← 삭제
		// 새로 만든 스택 페이지: swap에서 불러올 게 없음
		memset(kva, 0, PGSIZE);
		return true;
	}

    for (int i = 0; i < SWAP_SLOTS_CNT; i++) {
        disk_read(swap_disk, slot_idx * SWAP_SLOTS_CNT + i, kva + i * DISK_SECTOR_SIZE);
    }
    bitmap_reset(swap_slot, slot_idx);
	anon_page->slot_idx = BITMAP_ERROR;
    return true;
}
  • 수정된 anon_swap_in()에서는 slot_idx == BITMAP_ERROR인 경우 새 페이지에 대해 그냥 0으로 초기화시키고 true를 반환한다.
  • 9 → 5 of 141 tests failed.

위에서 file backed 함수들을 수정했지만 여전히 swap-file 테스트를 통과하지 못하는 문제가 발생했다.

backtrace result

Backtrace 결과 file_backed_destroy에서 에러가 발생한 것으로 보인다.

static void
file_backed_destroy (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;
	if (file_page->file == NULL)
		return;
    if (pml4_is_dirty(thread_current()->pml4, page->va)) {
        file_write_at(file_page->file, page->frame->kva, file_page->read_bytes, file_page->ofs);
        pml4_set_dirty(thread_current()->pml4, page->va, false);
    }
    pml4_clear_page(thread_current()->pml4, page->va);
	// list_remove(&page->frame->frame_elem); ← 삭제
}
  • Frame table에서 elem을 삭제하는 함수를 삭제(주석 처리)했더니 kernel panic이 발생하는 문제는 해결되었지만, 다른 에러가 발생했다.

bool
vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED, bool user UNUSED, bool write UNUSED, bool not_present UNUSED) {
	…
    if (page == NULL) {
		void *rsp = thread_current()->stack_pointer;
		if (rsp - PGSIZE < addr && addr < USER_STACK && rsp - PGSIZE >= STACK_LIMIT) {
			…
		} else {
			printf("[vm_try_handle_fault] rsp condition fault: %d %d %d\n", rsp - PGSIZE < addr, addr < USER_STACK, rsp - PGSIZE >= STACK_LIMIT);
			printf("%p %p\n", rsp - PGSIZE, addr);
			return false;
		}
	}
	…
}

디버깅 결과 rsp - PGSIZE < addr 조건 때문에 vm_try_handle_fault()에서 false가 리턴됨을 알게 되었다. 정확하게는, addr에 0x8이라는 이상한 주소가 들어가버려서 문제가 된 것이다.

static void
file_backed_destroy (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;
	if (file_page->file == NULL)
		return;
    if (pml4_is_dirty(thread_current()->pml4, page->va)) {
        file_write_at(file_page->file, page->frame->kva, file_page->read_bytes, file_page->ofs);
        pml4_set_dirty(thread_current()->pml4, page->va, false);
    }
    pml4_clear_page(thread_current()->pml4, page->va);
    /* 아래 코드들을 추가 */
	if (page->frame != NULL) {
		list_remove(&page->frame->frame_elem);
		palloc_free_page(page->frame->kva);
		free(page->frame);
		page->frame = NULL;
	}
}
  • file_backed_destroy()에서 앞서 삭제했던 코드들을 if (page->frame != NULL) 조건을 달아 다시 복구했다.
    • 위에서 해당 코드들을 주석 처리했던 이유는 assertion inode->deny_write_cnt > 0이 실패했기 때문이었다.
    • 프레임이 존재할 때만 free해주도록 수정해서 이미 없어진 파일을 참조하는 경우가 발생하지 않도록 했다.
  • 5 → 4 of 141 tests failed.


Page-merge 해결하기

이제 남은 것은 (Extra 과제인 copy-on-write를 제외하면) 3개의 page-merge 테스트들이다. 깃북에서도 이 테스트들이 무엇을 확인하는지에 대해 설명이 없어서, 무엇을 어떻게 건드려야 할지 파악하기도 쉽지 않아 보인다.

backtrace 결과

일단 backtrace한 결과를 보면 open()에서 문제가 생긴 것으로 보인다. 선배 기수 분들의 블로그에서는 시스템 콜에서 파일 시스템 관련 함수를 호출할 때 락을 걸어줘서 해결했다고 하는데, 우리 코드는 이미 파일 시스템 락을 걸고 해제하고 있는데도 page-merge 테스트들이 통과되지 못했다.

int open (const char *filename) {
	…
	if (!is_not_full) return -1; // Return -1 if fdt is full
	printf("is_not_full: %d\n", is_not_full);

	lock_acquire(&filesys_lock);
	printf("lock_acquire\n");
	struct file *file = filesys_open(filename);
	printf("filesys_open\n");
	lock_release(&filesys_lock);
	printf("lock_release\n");
    …
}

디버깅 결과 sort chunk 1에서 lock_acquire(&filesys_lock)이 제대로 수행되지 못하고 터져버렸다는 것을 알게 되었다.

'Krafton Jungle > 5. PintOS' 카테고리의 다른 글

[PintOS 5주차] Day 6-7  (0) 2025.06.11
[PintOS 5주차] Day 3  (0) 2025.06.07
[PintOS 5주차] Day 2  (0) 2025.06.06
[PintOS 5주차] Day 1  (0) 2025.06.05
[PintOS 4주차] Day 7  (0) 2025.06.04