[pintos] Week4~5: Virtual Memory - Part.5 Memory Mapped Files

이번 포스팅에서는 메모리 매핑된 페이지를 구현한다. 익명 페이지와 달리 메모리 매핑된 페이지는 파일 기반 매핑이다. 페이지 콘텐츠는 일부 기존 파일의 데이터를 미러링 한다. 페이지 폴트가 발생하면 물리적 프레임이 즉시 할당되고 내용이 파일에서 메모리로 복사된다. 메모리 매핑된 페이지가 unmapped 또는 swapped out 되면 콘텐츠의 모든 변경 사항이 파일에 반영된다.

메모리 매핑된 파일에 대한 두 가지 시스템 호출인 mmap 및 munmap을 구현한다. VM 시스템은 mmap 영역에서 페이지를 lazy load하고 mmap 된 파일 자체를 매핑을 위한 백업 저장소로 사용해야 한다. 이 두 시스템 콜을 구현하려면 vm/file.c에 정의된 do_mmap과 do_munmap을 구현해서 사용해야 한다.


구현 목표 

systemcall mmap, munmap 을 구현하고 이를 실제 처리하는 do_mmap과 do_munmap을 구현한다. 

파일 기반 페이지를 초기화 하고 제거하는 기능을 구현한다.


mmap 구현

  • Gitbook 가이드
더보기

fd로 열린 파일의 오프셋(offset) 바이트부터 length 바이트만큼을 프로세스의 가상주소공간의 주소 addr에 매핑합니다.

전체 파일은 addr에서 시작하는 연속 가상 페이지에 매핑됩니다.

파일 길이(length)가 PGSIZE의 배수가 아닌 경우 최종 매핑된 페이지의 일부 바이트가 파일 끝을 넘어 "stick out"됩니다.

  • 매핑 마지막 페이지가 파일 끝보다 클 경우, 남는 바이트는 0으로 채워야 함

page_fault가 발생하면 이 바이트를 0으로 설정하고 페이지를 디스크에 다시 쓸 때 버립니다. 성공하면 이 함수는 파일이 매핑된 가상 주소를 반환합니다. 실패하면 파일을 매핑하는 데 유효한 주소가 아닌 NULL을 반환해야 합니다.

  • 이 페이지를 디스크에 다시 쓸 때는 패딩된 0 바이트는 쓰지 말고 무시해야 함

fd로 열린 파일의 길이가 0바이트인 경우 mmap에 대한 호출이 실패할 수 있습니다. addr이 page-aligned 되지 않았거나, 기존 매핑된 페이지 집합(실행가능 파일이 동작하는 동안 매핑된 스택 또는 페이지를 포함)과 겹치는 경우 실패해야 합니다.

Linux에서 addr이 NULL이면 커널은 매핑을 생성할 적절한 주소를 찾습니다. 단순화를 위해 주어진 addr에서 mmap을 시도할 수 있습니다. 따라서 addr이 0이면 일부 Pintos 코드는 가상 페이지 0이 매핑되지 않는다고 가정하기 때문에 실패해야 합니다.

length가 0일 때도 mmap은 실패해야 합니다. 마지막으로 콘솔 입력 및 출력을 나타내는 파일 설명자는 매핑할 수 없습니다.

  • 성공 시: addr 반환
  • 실패 시: NULL 반환
  • 파일 크기 == 0
    • 아무것도 매핑할 수 없음 → 실패
  • addr이 page-aligned가 아님
    • 페이지 경계에 맞지 않음 → 실패
  • 기존 매핑과 겹침
    • addr ~ addr + length 범위에 이미 매핑된 페이지가 있으면 → 실패
    • 스택, 실행 파일 영역, 기존 mmap 영역 등과 충돌 체크 필요
  • addr == 0 (NULL 주소)
    • Linux에선 허용되지만, Pintos에선 NULL 페이지 접근 금지 가정 → 실패
  • length == 0
    • 매핑할 대상이 없음 → 실패
  •  콘솔 입출력 파일 디스크립터
    • fd == 0 (stdin) 또는 fd == 1 (stdout) 또는 fd == 2 (stderr) → 실패
    • 일반 파일이 아닌 입출력 → 매핑 불가

메모리 매핑된 페이지도 익명 페이지처럼 lazy load로 할당되어야 합니다.

page 구조체 수정

struct page {
	const struct page_operations *operations;	/* 페이지에 대한 동작 함수 (swap-in, swap-out 등) */
	void *va;              						/* 사용자 주소 공간의 가상 주소 */
	struct frame *frame;   						/* 이 페이지가 매핑된 물리 프레임 */

	/* Your implementation */
	/* 25.05.30 고재웅 작성 */
	struct hash_elem hash_elem;			// 해시 저장용 elem
	bool writable; 						// 쓰기 가능한 페이지 인지
	bool is_loaded;						// 실제로 프레임에 로드되어 있는지
	int mapped_page_count; 				// 매핑된 페이지 개수
	/* union은 여러 타입 중 하나만을 저장할 수 있는 특수한 자료형으로,  
	 * 타입별 데이터는 union에 바인딩 됩니다. 각 함수는 현재 union을 자동으로 감지합니다. */
	union {
		struct uninit_page uninit;
		struct anon_page anon;
		struct file_page file;
#ifdef EFILESYS
		struct page_cache page_cache;
#endif
	};
};
  • mapped_page_count를 추가해서 매핑된 페이지 개수를 별도로 기록한다. 이 필드는 munmap시 사용

mmap system call 선언

  • userprog/syscall.c
 case SYS_MMAP:
        f->R.rax = mmap(f->R.rdi, f->R.rsi, f->R.rdx, f->R.r10, f->R.r8);
        break;
    case SYS_MUNMAP:
        munmap(f->R.rdi);
        break;
  • syscall에 SYS_MMAP과 SYS_MUNMAP 선언
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset){
    // TODO: 1. 유효성 검사
    // - addr이 NULL이 아니고 page-aligned인지 확인
    if (!is_user_vaddr(addr) || !is_user_vaddr(addr + length))
		return NULL;

    // - offset이 PGSIZE의 배수인지 확인
    if (offset % PGSIZE != 0)
        return NULL;
        
    if (pg_round_down(addr) != addr)
        return NULL;
    
    // - fd가 유효하고 콘솔 stdin/stdout/stderr이 아니어야 함
    if (fd < 3)
        return NULL;

    struct file *file = process_get_file(fd);
    if (file == NULL)
        return NULL;

    // - length가 0이 아니어야 함
    if (file_length(file) == 0 || (int)length <= 0)
        return NULL;

    return do_mmap(addr, length, writable, file, offset);
}

do_mmap 구현

  • vm/file.c
/* Do the mmap */
void *
do_mmap (void *addr, size_t length, int writable,
		struct file *file, off_t offset) {
	
    // TODO: 2. fd에 대응하는 struct file * 구하기
    // - 열린 파일 디스크립터 테이블에서 찾고, 실패 시 NULL 반환
    // - file을 reopen하여 별도 참조를 유지 (중복 닫힘 방지)
	struct file *f = file_reopen(file);
	int total_page_count = length / PGSIZE;
	if (length % PGSIZE != 0)
		total_page_count += 1;
	void *start_addr = addr;

	/* 여는 파일이 length보다 작으면 그냥 file_length 사용
	 * 만약 5000바이트 짜리를 매핑해야 한다면 첫 페이지에 4096바이트 두번째 페이지에 904 바이트를 읽고
	 * 나머지 3192 바이트는 0으로 채워야 한다. 
	 */
	size_t read_bytes = file_length(f) < length? file_length(f) : length;
	size_t zero_bytes = PGSIZE - read_bytes % PGSIZE;		

	// TODO: 3. 파일 길이 검사 및 전체 매핑 길이 조정
    // - 파일 길이를 구하고, 파일 끝까지 매핑 가능한지 확인
    // - length가 파일 길이보다 크면 남는 부분은 0으로 채우도록 기록
	ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); 	// 전체 매핑 크기가 페이지 크기의 배수여야 함을 보장한다.
	ASSERT (pg_ofs (addr) == 0);					  	// 페이지 오프셋이 0 즉 page_aligned address 임을 보장
	ASSERT (offset % PGSIZE == 0);							// 파일 내의 오프셋 ofs도 페이지 크기의 배수여야한다.
    

    // TODO: 4. 페이지 단위로 loop를 돌며 각 가상 페이지를 uninit으로 등록
    // - vm_alloc_page_with_initializer() 사용
    // - 이 때 lazy_load_file을 initializer로 넘김
    // - struct file_info(aux)에 file, offset, read_bytes 등 저장

    // TODO: 5. 모든 페이지가 성공적으로 매핑되었으면 addr 반환
    // - 실패 시 중간에 등록한 페이지들을 모두 해제하고 NULL 반환

	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. */
		struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
		lazy_load_arg->file = f;					 // 내용이 담긴 파일 객체
		lazy_load_arg->ofs = offset;					 // 이 페이지에서 읽기 시작할 위치
		lazy_load_arg->read_bytes = page_read_bytes; // 이 페이지에서 읽어야 하는 바이트 수
		lazy_load_arg->zero_bytes = page_zero_bytes; // 이 페이지에서 read_bytes만큼 읽고 공간이 남아 0으로 채워야 하는 바이트 수

		if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_segment , lazy_load_arg))
			return NULL;
		struct page *p = spt_find_page(&thread_current()->spt, start_addr);
		p->mapped_page_count = total_page_count;
		
		/* Advance. */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		addr += PGSIZE;
		offset += page_read_bytes;
	}
	return start_addr;
}

do_mmap 은 메모리 매핑(mmap) 기능을 구현한 것이다. 파일의 내용을 가상 메모리에 매핑하여 직접 접근할 수 있도록 준비하는 작업을 수행한다. 

즉, 파일 내용을 메모리에 매핑하면 파일 I/O 없이도 메모리처럼 읽고 쓸 수 있게  되며, 페이지 폴트가 발생했을 때 lazy loading으로 데이터를 로드하게 된다.

struct file *f = file_reopen(file);
int total_page_count = length / PGSIZE;
if (length % PGSIZE != 0)
    total_page_count += 1;
  • 같은 파일을 여러 번 매핑할 수 있으므로 file_reopen()으로 별도 참조 생성
  • 전체 매핑에 필요한 페이지 수 계산
size_t read_bytes = file_length(f) < length ? file_length(f) : length;
size_t zero_bytes = PGSIZE - read_bytes % PGSIZE;
  • 매핑할 전체 크기가 파일 크기보다 크면, 나머지는 0으로 채워야 함
  • 마지막 페이지에서의 zero-fill을 계산
ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
ASSERT (pg_ofs (addr) == 0);
ASSERT (offset % PGSIZE == 0);
  • 페이지 경계 단위로 정렬되어야 하며, 파일 오프셋도 페이지 단위로 정렬되어야 함
while (read_bytes > 0 || zero_bytes > 0) {
	...
	struct lazy_load_arg *lazy_load_arg = malloc(sizeof(struct lazy_load_arg));
	lazy_load_arg->file = f;
	lazy_load_arg->ofs = offset;
	lazy_load_arg->read_bytes = page_read_bytes;
	lazy_load_arg->zero_bytes = page_zero_bytes;

	if (!vm_alloc_page_with_initializer(VM_FILE, addr, writable, lazy_load_segment, lazy_load_arg))
		return NULL;
  • 페이지마다 lazy_load_arg 구조체를 만들어 로딩 정보를 저장
  • 각 페이지는 lazy loading용 VM_FILE 타입으로 등록
  • lazy_load_segment()가 접근 시 실제 파일 내용을 메모리에 읽어올 것임
struct page *p = spt_find_page(&thread_current()->spt, start_addr);
p->mapped_page_count = total_page_count;
  • 매핑된 전체 페이지 수를 페이지 구조체에 저장
  • 이 정보는 munmap()에서 전체 매핑 영역을 해제할 때 사용될 수 있음
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
addr += PGSIZE;
offset += page_read_bytes;
  • 다음 페이지에 대한 설정을 준비
return start_addr;
  • 성공 시 시작 주소 반환

munmap 구현

  • Gitbook 가이드
더보기

지정된 주소 범위 addr에 대한 매핑을 해제합니다. 지정된 주소는 아직 매핑 해제되지 않은 동일한 프로세서의 mmap에 대한 이전 호출에서 반환된 가상 주소여야 합니다.

  • munmap(addr)에서 주어진 주소는 이전에 do_mmap()을 통해 파일이 매핑된 주소여야 함
  • 즉, 유효한 매핑이 존재하는 주소만 munmap() 가능 

종료를 통하거나 다른 방법을 통해 프로세스가 exit 되면 모든 매핑이 암시적으로 매핑 해제됩니다. 암시적이든 명시적이든 매핑이 매핑 해제되면 프로세스에서 쓴 모든 페이지는 파일에 다시 기록되며 기록되지 않은 페이지는 기록되지 않아야 합니다. 그런 다음 해당 페이지는 프로세스의 가상 페이지 목록에서 제거됩니다.

  • 프로세스가 종료될 때 supplemental_page_table_kill() 또는 process_exit() 내부에서
    모든 mmap 영역이 자동으로 munmap 처리
  • 즉, 명시적 호출 없이도 깨끗하게 정리되어야 함
  • 매핑된 페이지에 유저가 값을 써서 수정한 경우 → 파일에 저장되어야 함
  • 그렇지 않으면 변경사항이 손실됨
  • userprog/syscall.c
void munmap (void *addr){
	do_munmap(addr);
}
  • vm/file.c
void
do_munmap (void *addr) {
	struct supplemental_page_table *spt = &thread_current()->spt;
	struct page *p = spt_find_page(spt, addr);
    int count = p->mapped_page_count;
	for (int i = 0; i < count; i++){
		if (p)
			destroy(p);
		addr += PGSIZE;
		p = spt_find_page(spt, addr);
	}
}
struct supplemental_page_table *spt = &thread_current()->spt;
struct page *p = spt_find_page(spt, addr);
  • 현재 스레드의 보조 페이지 테이블(SPT)에서 주어진 주소에 해당하는 페이지(p)를 찾음
  • 이 주소는 do_mmap()이 반환했던 가상 주소여야 하며, SPT에 등록되어 있어야 함
int count = p->mapped_page_count;
  • 이 매핑이 차지하는 전체 페이지 수
  • do_mmap()에서 첫 페이지에 mapped_page_count를 저장했으므로, 반복 횟수를 알 수 있음
for (int i = 0; i < count; i++) {
	if (p)
		destroy(p);
	addr += PGSIZE;
	p = spt_find_page(spt, addr);
}
  • destroy(p)는 해당 페이지에 대해:
    • 파일로 write-back 수행 (필요한 경우)
    • 프레임 반환
    • SPT 해시에서 제거
    • 구조체 자체 해제
  • 다음 페이지 주소로 addr을 증가시키며 모든 매핑된 페이지를 반복 제거

File Page Init && Destroy 구현

file_backed_initializer 구현

  • include/vm/file.h
struct file_page {
	struct file *file;
	off_t ofs;
	uint32_t read_bytes;
	uint32_t zero_bytes;
};
  • vm/file.c
/* Initialize the file backed page */
bool
file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
	/* Set up the handler */
	page->operations = &file_ops;

	struct file_page *file_page = &page->file;

	// TODO: page에 page->uninit.aux에 들어있는 정보를 구조체로 형변환 (ex. lazy_load_arg *)
	struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)page->uninit.aux;
	file_page->file = lazy_load_arg->file;
	file_page->ofs = lazy_load_arg->ofs;
	file_page->read_bytes = lazy_load_arg->read_bytes;
	file_page->zero_bytes = lazy_load_arg->zero_bytes;
	return true;
}

lazy loading 초기화 과정에서 실제 파일 매핑 정보를 해당 페이지에 전달하기 위해 initializer를 사용한다.

page->operations = &file_ops;
  • 해당 페이지가 파일 기반(VM_FILE) 페이지임을 지정
  • 이후 swap_in, destroy, write_back 등의 동작은 file_ops를 통해 처리됨
struct file_page *file_page = &page->file;
  • struct page 안에 포함된 struct file_page에 접근
  • 이 구조체는 파일 매핑 관련 정보를 저장하는 공간
struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)page->uninit.aux;
  • 이 페이지는 처음 등록될 때 lazy loading용으로 등록되었기 때문에,
    초기화 정보는 page->uninit.aux에 저장되어 있음
  • 여기엔 struct lazy_load_arg 타입의 정보가 들어 있음:
struct lazy_load_arg {
    struct file *file;
    off_t ofs;
    size_t read_bytes;
    size_t zero_bytes;
};
file_page->file = lazy_load_arg->file;
file_page->ofs = lazy_load_arg->ofs;
file_page->read_bytes = lazy_load_arg->read_bytes;
file_page->zero_bytes = lazy_load_arg->zero_bytes;
  • 이 페이지가 추후 swap_in()될 때 어떤 파일에서 어느 부분을 읽을 것인지 설정

file_backed_destroy 구현

  • vm/file.c
static void
file_backed_destroy (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;
	// TODO: 해당 페이지가 dirty 상태인지 확인
    // - pml4_is_dirty() 또는 page->frame->is_dirty 사용
	if (pml4_is_dirty(thread_current()->pml4, page->va)){
		// TODO: dirty라면, 파일에 해당 내용을 file_write_at()으로 저장
		// - page->va, aux->file, aux->offset 등에서 정보 추출
		// - writable 여부도 확인
		file_write_at(file_page->file, page->va, file_page->read_bytes, file_page->ofs);
		pml4_set_dirty(thread_current()->pml4, page->va, 0);
	}
	pml4_clear_page(thread_current()->pml4, page->va);
}
if (pml4_is_dirty(thread_current()->pml4, page->va)) {
  • 현재 프로세스의 page table (pml4)에서 이 페이지가 dirty(수정됨) 상태인지 확인
  • dirty란?
    유저 프로그램이 이 페이지에 write 접근하여 내용을 변경했음을 의미
file_write_at(file_page->file, page->va, file_page->read_bytes, file_page->ofs);
  • 수정된 페이지 내용을 파일로 다시 씀
    • 대상 파일: file_page->file
    • 메모리 주소: page->va (현재 페이지의 가상 주소, 커널에서 직접 접근 가능)
    • 바이트 수: file_page->read_bytes (원래 파일에서 읽은 양만큼만 기록)
    • 파일 내 오프셋: file_page->ofs
  • 이후 dirty 상태 플래그 초기화:
pml4_set_dirty(thread_current()->pml4, page->va, 0);
pml4_clear_page(thread_current()->pml4, page->va);
  • 페이지 테이블에서 해당 페이지 매핑 제거
  • 이후에는 SPT에서 제거하거나 프레임을 해제해도 안전

기타 수정

  • vm/vm.c
/* Copy supplemental page table from src to dst */
bool
supplemental_page_table_copy (struct supplemental_page_table *dst UNUSED,
		struct supplemental_page_table *src UNUSED) 
{
	struct hash_iterator i;
	hash_first(&i, &src->pages);
	while (hash_next(&i))
	{
		// src_page 정보
		struct page *src_page = hash_entry(hash_cur(&i), struct page, hash_elem);
		enum vm_type type = src_page->operations->type;
		void *upage = src_page->va;
		bool writable = src_page->writable;

		/* 1) type이 uninit이면 */
		if (type == VM_UNINIT)
		{ // uninit page 생성 & 초기화
			vm_initializer *init = src_page->uninit.init;
			void *aux = src_page->uninit.aux;
			vm_alloc_page_with_initializer(VM_ANON, upage, writable, init, aux);
			continue;
		}
		if (type == VM_FILE){
			struct lazy_load_arg *file_aux = malloc(sizeof(struct lazy_load_arg));
            file_aux->file = src_page->file.file;
            file_aux->ofs = src_page->file.ofs;
            file_aux->read_bytes = src_page->file.read_bytes;
            file_aux->zero_bytes = src_page->file.zero_bytes;
            if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, file_aux))
                return false;
            struct page *file_page = spt_find_page(dst, upage);
            file_backed_initializer(file_page, type, NULL);
            file_page->frame = src_page->frame;
            pml4_set_page(thread_current()->pml4, file_page->va, src_page->frame->kva, src_page->writable);
            continue;
		}

		/* 2) type이 uninit이 아니면 */
		if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, NULL)) // uninit page 생성 & 초기화
			// init(lazy_load_segment)는 page_fault가 발생할때 호출됨
			// 지금 만드는 페이지는 page_fault가 일어날 때까지 기다리지 않고 바로 내용을 넣어줘야 하므로 필요 없음
			return false;

		// vm_claim_page으로 요청해서 매핑 & 페이지 타입에 맞게 초기화
		if (!vm_claim_page(upage))
			return false;

		// 매핑된 프레임에 내용 로딩
		struct page *dst_page = spt_find_page(dst, upage);
		memcpy(dst_page->frame->kva, src_page->frame->kva, PGSIZE);
	}
	return true;
}

새로 File 페이지에 대한 기능을 추가하고 있으니 이에 맞추어 supplemental_page_table_copy 함수도 수정한다.

if (type == VM_FILE) {
    struct lazy_load_arg *file_aux = malloc(sizeof(struct lazy_load_arg));
    file_aux->file = src_page->file.file;
    file_aux->ofs = src_page->file.ofs;
    file_aux->read_bytes = src_page->file.read_bytes;
    file_aux->zero_bytes = src_page->file.zero_bytes;
  • 원본 페이지(src_page)의 file_page 정보를 읽어서 새 aux 구조체를 생성
  • 이 구조체는 lazy loading 시 필요한 파일 정보들을 담고 있음
  • 이를 통해 자식도 lazy 방식으로 같은 파일을 매핑할 수 있음
if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, file_aux))
    return false;
  • 자식 SPT에 새로운 VM_FILE 페이지 등록
  • initializer는 NULL이지만 aux를 통해 정보를 전달
struct page *file_page = spt_find_page(dst, upage);
file_backed_initializer(file_page, type, NULL);
  • 일반적으로 lazy load 시 file_backed_initializer()는 최초 접근 시 호출됨
  • 하지만 fork 시점에서는 직접 초기화해 줘야 즉시 사용 가능하므로 명시 호출함
file_page->frame = src_page->frame;
pml4_set_page(thread_current()->pml4, file_page->va, src_page->frame->kva, src_page->writable);
continue;
  • 자식 페이지에 부모의 프레임을 그대로 공유시킴
    (file-backed 페이지는 일반적으로 공유 가능하므로, 이 방식이 유효함)
  • 페이지 테이블에 가상 주소–물리 주소 매핑 등록
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: 페이지를 생성하고, VM 유형에 따라 초기화 파일을 가져옵니다.Add commentMore actions
		 * TODO: 그런 다음 uninit_new를 호출하여 "uninit" 페이지 구조체를 생성합니다.
		 * TODO: uninit_new를 호출한 후 필드를 수정해야 합니다. */
		struct page *p = (struct page *)malloc(sizeof(struct page));
		if (p == NULL){
			return false;
		}
		bool (*page_initializer)(struct page *, enum vm_type, void *);

		switch (VM_TYPE(type))
		{
			case VM_ANON:
				page_initializer = anon_initializer;
				break;
			case VM_FILE:
				page_initializer = file_backed_initializer;
				break;
			default:
				free(p);
				return false;
		}
		/* TODO: spt에 페이지를 삽입합니다. */
		uninit_new(p, upage, init, type, aux, page_initializer);
		p->writable = writable;

		return spt_insert_page(spt, p);
	}
err:
	return false;
}
  • switch 문의 default를 설정했다.

run: two phys addrs should be the same.: FAILED
pass tests/userprog/args-none
pass tests/userprog/args-single
pass tests/userprog/args-multiple
pass tests/userprog/args-many
pass tests/userprog/args-dbl-space
pass tests/userprog/halt
pass tests/userprog/exit
pass tests/userprog/create-normal
pass tests/userprog/create-empty
pass tests/userprog/create-null
pass tests/userprog/create-bad-ptr
pass tests/userprog/create-long
pass tests/userprog/create-exists
pass tests/userprog/create-bound
pass tests/userprog/open-normal
pass tests/userprog/open-missing
pass tests/userprog/open-boundary
pass tests/userprog/open-empty
pass tests/userprog/open-null
pass tests/userprog/open-bad-ptr
pass tests/userprog/open-twice
pass tests/userprog/close-normal
pass tests/userprog/close-twice
pass tests/userprog/close-bad-fd
pass tests/userprog/read-normal
pass tests/userprog/read-bad-ptr
pass tests/userprog/read-boundary
pass tests/userprog/read-zero
pass tests/userprog/read-stdout
pass tests/userprog/read-bad-fd
pass tests/userprog/write-normal
pass tests/userprog/write-bad-ptr
pass tests/userprog/write-boundary
pass tests/userprog/write-zero
pass tests/userprog/write-stdin
pass tests/userprog/write-bad-fd
pass tests/userprog/fork-once
pass tests/userprog/fork-multiple
pass tests/userprog/fork-recursive
pass tests/userprog/fork-read
pass tests/userprog/fork-close
pass tests/userprog/fork-boundary
pass tests/userprog/exec-once
pass tests/userprog/exec-arg
pass tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
pass tests/userprog/exec-read
pass tests/userprog/wait-simple
pass tests/userprog/wait-twice
pass tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
pass tests/userprog/multi-recurse
pass tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
pass tests/userprog/rox-child
pass tests/userprog/rox-multichild
pass tests/userprog/bad-read
pass tests/userprog/bad-write
pass tests/userprog/bad-read2
pass tests/userprog/bad-write2
pass tests/userprog/bad-jump
pass tests/userprog/bad-jump2
pass tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
pass tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
pass tests/vm/pt-write-code
pass tests/vm/pt-write-code2
pass tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
FAIL tests/vm/page-merge-par
FAIL tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
pass tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
pass tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
pass tests/vm/swap-fork
pass tests/filesys/base/lg-create
pass tests/filesys/base/lg-full
pass tests/filesys/base/lg-random
pass tests/filesys/base/lg-seq-block
pass tests/filesys/base/lg-seq-random
pass tests/filesys/base/sm-create
pass tests/filesys/base/sm-full
pass tests/filesys/base/sm-random
pass tests/filesys/base/sm-seq-block
pass tests/filesys/base/sm-seq-random
pass tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
pass tests/filesys/base/syn-write
pass tests/threads/alarm-single
pass tests/threads/alarm-multiple
pass tests/threads/alarm-simultaneous
pass tests/threads/alarm-priority
pass tests/threads/alarm-zero
pass tests/threads/alarm-negative
pass tests/threads/priority-change
pass tests/threads/priority-donate-one
pass tests/threads/priority-donate-multiple
pass tests/threads/priority-donate-multiple2
pass tests/threads/priority-donate-nest
pass tests/threads/priority-donate-sema
pass tests/threads/priority-donate-lower
pass tests/threads/priority-fifo
pass tests/threads/priority-preempt
pass tests/threads/priority-sema
pass tests/threads/priority-condvar
pass tests/threads/priority-donate-chain
FAIL tests/vm/cow/cow-simple
7 of 141 tests failed.