[목표]
익명 페이지 관련된 내용을 이해하고 코드를 작성해봅니다. (진행중)
fork를 제외하고 모든 테스트 케이스가 돌아가는지 확인해봅니다. (진행중)
새로운 헤드폰을 언박싱했습니다. 해외에서 배송이와서 좀 오래걸렸네요…

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에서 사용
};
anon_initializer 를 수정하고 있습니다.
오늘은 랜덤 런치날이라서 다른 팀원분들과 식사를 했습니다.

지금 확인해본 결과 anon_initializer 와 vm_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_ops를 get_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폴트가 나오는것 같다.
운동(등, 복근)을 마치고 돌아왔습니다.
식사를 하고 약간의 휴식을 가졌습니다.

아까 전에 이어서 확인을 해보았습니다.
process.c의 load_segment 와 lazy_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;
}
권호형과 함께 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);
}
이렇게 작성하니 결과값은 찍힌다. 정상적인 결과값이 안나올 뿐…
퇴근하고 노션과 벨로그를 적고 있다. 밀린게 있어서 적어야한다…