WEEK 12 PintOS TIL(6월4일 수요일)

Devkty·2025년 6월 5일

[목표]
익명 페이지 관련된 내용을 이해하고 코드를 작성해봅니다. (진행중)
fork를 제외하고 모든 테스트 케이스가 돌아가는지 확인해봅니다. (진행중)

09:55 ~ 10:30

새로운 헤드폰을 언박싱했습니다. 해외에서 배송이와서 좀 오래걸렸네요…

10:30 ~ 11:30

vm_anon_init을 구현해봤는데, 이게 맞는지 모르겠습니다. 초기화를 해야되서 swap_slot을 -1로 해야되는건 알겠는데 받는 인자나 내보내는 값의 형태를 void에서 bool로 바꾸고 struct page *page, enum vm_type type, void *kva 로 인자를 바꿔서 괜찮은지 모르겠습니다.

bool vm_anon_init(struct page *page, enum vm_type type, void *kva) {
    // 1. page_operations 설정
    page->operations = &anon_ops;

    // 2. anon_page 필드 초기화
    struct anon_page *anon = &page->anon;
    anon->swap_slot = -1;  // 아직 스왑되지 않았음을 나타냄

    return true;  // 성공적으로 초기화되었음을 알림
}

anon.h의 구조체도 일부분 추가했다.

struct anon_page {
    int swap_slot;  // anon.c vm_anon_init에서 사용
};

11:30 ~ 12:00

anon_initializer 를 수정하고 있습니다.

12:00 ~ 13:00

오늘은 랜덤 런치날이라서 다른 팀원분들과 식사를 했습니다.

13:00 ~ 14:00

지금 확인해본 결과 anon_initializervm_anon_init 는 수정하지 않는걸로 결정 했습니다. 일단은 롤백을 진행했습니다.

load_segment 을 진행해보겠습니다.

vm_claim_page 픽스

bool
vm_claim_page (void *va UNUSED) {
	struct page *page = NULL;
	/* TODO: Fill this function */
	// 기능을 구현하세요.
	if(page -> va == NULL) return false;

	page = spt_find_page(&thread_current() -> spt, va);  // spt 페이지를 가져옵니다.

	return vm_do_claim_page (page);
}
bool
spt_insert_page (struct supplemental_page_table *spt UNUSED,
		struct page *page UNUSED) {
	int succ = false;
	struct page *va;  // page 구조체에 있는 va를 가져온다.

	/* TODO: Fill this function. */
	// 먼저, 주어진 보충 테이블에 가상주소가 존재하는지 확인해야한다.
	page = spt_find_page(spt, va);
	if (page == NULL) {
		struct hash_elem *inserted = hash_insert(&spt -> spt_hash, &page -> hash_elem);  // 만약, 페이지가 비었다면 spt에 페이지를 삽입합니다.
	}
	else 
		return false;

	return succ;
}

위의 page의 struct 내용 삭제 위에 이미 선언이 있으므로.

vm_alloc_page_with_initializer 에서 문제가 있었다.

anon_ops와 file_ops가 본파일에 static으로 선언되어 있는데 vm.c에는 const로 선언되어 문제가 생긴것이었다. 근데 이 본파일의 선언은 수정하지 말라고 되어 있어서 다른 함수를 만들었다.

enum vm_type real_type = VM_TYPE(type);
const struct page_operations *appropriate_ops;

		if (real_type == VM_ANON) {
			init = anon_initializer;
			appropriate_ops = &anon_ops;  // 사용을 위한 anon.h 선언
		}
		else if (real_type == VM_FILE) {
			init = file_backed_initializer;
			appropriate_ops = &file_ops;  // 사용을 위한 file.h 선언
		}
		else {
			return false;   // 지원하지 않는 타입일때
		}

그래서 각 본 파일에 getter 함수를 추가 했다. anon.c

// 위의 구조체를 외부 접근용 getter 함수 제공 (static 조건 해결)
const struct page_operations *get_anon_ops(void) {
	return &anon_ops;
}

물론 헤더 파일도 바꿔야한다. anon.h

const struct page_operations *get_anon_ops(void);  // 정적 구조체 변경 불가로 추가 선언

vm.c의 일부분도 해당 getter 함수에 맞게 수정해야한다.

appropriate_ops = &anon_ops; 에서 &anon_opsget_anon_ops 로 바꾸면된다.

file.c와 file.h도 동일하게 바꾸면 된다.

다 바꾸고 나니 테스트 케이스는 정상 작동되는데

이런식으로 오류가 나온다… 대충봐선 NULL을 처리하질 못하는것 같은데 페이지 폴트를 잘 못넘기는 건지… 한번 확인해봐야될 것 같다. → 확인해보니 프레임 가져옴에 문제가 생겨 제대로 가져오지 못하는 것 같다.

이제는 디버깅과의 싸움이다.

먼저, vm_get_frame 에서 list_push_back을 하기 위해서 프레임 테이블 락을 거는데, 이걸 vm_init에서 잘못 초기화해서 바꿔줬다.

void
vm_init (void) {
	vm_anon_init ();
	vm_file_init ();
#ifdef EFILESYS  /* For project 4 */
	pagecache_init ();
#endif
	register_inspect_intr ();
	/* DO NOT MODIFY UPPER LINES. */
	// ※ 위의 라인은 수정하지마세요. ※
	/* TODO: Your code goes here. */
	list_init(&frame_table);
	list_init(&frame_table_lock);   // 테스트케이스 실행 이후 트러블 슈팅
}

기존의 list_init(&frame_table_lock);lock_init(&frame_table_lock); 으로 바꿨다.

그랬는데, 다른 문제가 나타났다.
쓰레드 관련 문제인 것 같다.

현재까지 확인 결과 vm_do_claim_page 에서 못가져와서 page폴트가 나오는것 같다.

16:30 ~ 18:00

운동(등, 복근)을 마치고 돌아왔습니다.

18:00 ~ 19:00

식사를 하고 약간의 휴식을 가졌습니다.

19:00 ~ 22:00

아까 전에 이어서 확인을 해보았습니다.
process.c의 load_segmentlazy_load_segment 함수가 제대로 구현되지 않아서 생긴 문제였습니다.

먼저 깃북에 따라 load_segment 를 구현해 보겠습니다.
lazy_load_segment 에 정보를 전달하기 위해 aux를 설정해야합니다.

struct lazy_load_info {    // lazy를 로드하기 위해 인포를 받음
	struct file *file;
	off_t ofs;
	uint8_t *upage;
	uint32_t read_bytes;
	uint32_t zero_bytes;
	bool writable;
};

load_segment에는 일부분만 바꿨습니다. vm_alloc_page_with_initializer 에 lazy 페이지 정보를 넘기기 위해 aux 를 통해 전달했습니다.

// OFS 오프셋에서 시작하는 세그먼트를 FILE의 UPAGE 주소에 로드합니다. 
// 총 READ_BYTES + ZERO_BYTES 바이트의 가상 메모리가 다음과 같이 초기화됩니다.
// UPAGE의 READ_BYTES 바이트는 OFS 오프셋에서 시작하는 FILE에서 읽어야 합니다.
// UPAGE + READ_BYTES의 ZERO_BYTES 바이트는 0으로 초기화해야 합니다.
// 이 함수로 초기화된 페이지는 WRITABLE이 true이면 사용자 프로세스가 쓸 수 있어야 하고, 
// 그렇지 않으면 읽기 전용이어야 합니다.
// 성공하면 true를 반환하고, 메모리 할당 오류 또는 디스크 읽기 오류가 발생하면 false를 반환합니다.
static bool
load_segment(struct file *file, off_t ofs, uint8_t *upage,
			 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
	ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
	ASSERT(pg_ofs(upage) == 0);
	ASSERT(ofs % PGSIZE == 0);

	while (read_bytes > 0 || zero_bytes > 0)
	{
		/* Do calculate how to fill this page.
		 * We will read PAGE_READ_BYTES bytes from FILE
		 * and zero the final PAGE_ZERO_BYTES bytes. */
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
		size_t page_zero_bytes = PGSIZE - page_read_bytes;

		/* TODO: Set up aux to pass information to the lazy_load_segment. */
		// lazy_load_segment에 정보를 전달하기 위해 aux를 설정합니다.
		// void *aux = NULL;
		struct lazy_load_info *aux = malloc(sizeof(struct lazy_load_info));
		aux -> file = file;
		aux -> ofs = ofs;
		aux -> read_bytes = page_read_bytes;
		aux -> zero_bytes = page_zero_bytes;
		aux -> writable = writable;
		
		if (!vm_alloc_page_with_initializer(VM_ANON, upage,
											writable, lazy_load_segment, aux))
			return false;

		/* Advance. */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
	}
	return true;
}

이제는 lazy_load_segment를 작성해보겠습니다.

static bool
lazy_load_segment(struct page *page, void *aux)
{
	/* 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. */
	// 할 일: 파일에서 세그먼트를 로드 해야합니다.
	// 할 일: 이것은 주소 VA에서 첫 번째 페이지 오류가 발생할 때 호출됩니다.
	// 할 일: 이 함수를 호출할 때 VA를 사용할 수 있습니다.

	// filesys/file.c 에 있는 file_read_at을 사용해야된다고 한다.
	struct lazy_load_info *info = aux;
	void *kva = page -> frame -> kva;
	
	// 파일 읽기 검증 (실제 바이트와 읽어야하는 info바이트 수 비교)
	if (file_read_at(info -> file, kva, info -> read_bytes, info -> ofs) != (int) info -> read_bytes)
		return false;

	memset(kva + info -> read_bytes, 0, info -> zero_bytes);

	free(info);
	return true;
}

22:00 ~ 03:00

권호형과 함께 spt_insert_page 관련 문제 해결했습니다.

bool
spt_insert_page (struct supplemental_page_table *spt UNUSED,
		struct page *page UNUSED) {
	int succ = false;
	// struct page *va;  // page 구조체에 있는 va를 가져온다.
	void *va = page->va;  // 쓰레드 관련 트러블 슈팅 해결중
	
	/* TODO: Fill this function. */
	//먼저, 주어진 보충 테이블에 가상주소가 존재하는지 확인해야한다.
	struct page *p = spt_find_page(spt, va);

	if (p == NULL) {
		
		struct hash_elem *inserted = hash_insert(&spt->spt_hash, &page->hash_elem);  // 만약, 페이지가 비었다면 spt에 페이지를 삽입합니다.
		succ = true;
	}
	else return false;
	return succ;
}

확인 결과 setup_stack을 구현하지 않아서 구현해보려한다.

uninit.c 가 문제였다. 추가로 작성한 부분은 주석처리했다.

/* Initalize the page on first fault */
// 첫 번째 오류 발생 시 페이지 초기화합니다.
static bool
uninit_initialize (struct page *page, void *kva) {
	struct uninit_page *uninit = &page->uninit;

	/* Fetch first, page_initialize may overwrite the values */
	// Fetch를 먼저하세요. page_initialize가 값을 덮어쓸 수 있습니다.
	// 필요한 값 먼저 복사
	vm_initializer *init = uninit->init;
	void *aux = uninit->aux;

	// // 타입에 맞게 페이지 초기화
	// if (!uninit -> page_initializer(page, uninit->type, kva))
	// 	return false;

	// // 사용자 정의 초기화 함수가 있다면 호출
	// if (init != NULL) {
	// 	if (!init(page, aux)) {
	// 		if (page -> frame != NULL) {
	// 			palloc_free_page(page -> frame -> kva);  // 오류 발생시 페이지 해제
	// 			}
	// 			return false;
	// 	}
	// }
	// return true;

	/* TODO: You may need to fix this function. */
	// 이 기능을 수정해야 할 수도 있습니다.
	return uninit->page_initializer (page, uninit->type, kva) &&
		(init ? init (page, aux) : true);
}

이렇게 작성하니 결과값은 찍힌다. 정상적인 결과값이 안나올 뿐…

03:00 ~ 04:00

퇴근하고 노션과 벨로그를 적고 있다. 밀린게 있어서 적어야한다…

profile
모든걸 기록하며 성장하고 싶은 개발자입니다. 현재 크래프톤 정글 8기를 수료하고 구직활동 중입니다.

0개의 댓글