WIL - PintOS:Project 3 / VM - Memory Management

박상우·2024년 6월 29일
0

📝 TIL

목록 보기
40/45
post-thumbnail

⚽️ 목표

가상 메모리 페이지(Virtual Page)와 물리 메모리 프레임 (Physical Frame)를 효과적으로 관리해야한다.

→ 가상 / 물리 메모리 영역을 누가(프로세스?)가 사용했고, 어떤 목적(?)으로 사용했는지 기억해야한다.

기존 page table에서 다루지 않는 정보들(누가, 어떤 목적)을 위한 부수적인 테이블이 필요하다. 그래서 supplement page table을 다루고, 이후 물리 메모리 Page Frame을 다루어야한다.

🤨 page Struct

페이지는 크게 세가지 종류로 나누어 다룬다.(VM_UINIT, VM_ANON, VM_FILE) 그리고 하나의 페이지는 swap_in, swap_out, destroy를 호출한다. 세가지 함수의 동작이 페이지의 종류에 따라 다르기 때문에 타입별로 호출하는 함수를 swich-case함수를 통해 불러야한다.

c에서 사용하는 Struct의 경우 멤버 변수, 즉 특정 값만 가질 수 있기 때문에 함수의 포인터를 저장하여 타입별로 포인터가 가리키는 함수를 다르게 하여 타입별 실행하는 함수들을 다르게 적용할 수 있다.

struct page {
	const struct page_operations *operations;
	void *va;              /* Address in terms of user space */
	struct frame *frame;   /* Back reference for frame */ // 프레임 역참조

	/* Your implementation */

	/* Per-type data are binded into the union.
	 * Each function automatically detects the current union */
	union {
		struct uninit_page uninit;
		struct anon_page anon;
		struct file_page file;
	#ifdef EFILESYS
		struct page_cache page_cache;
	#endif
	};
};

세가지 타입에 대해서 페이지 초기화 과정이 모두 다르다.

이 부분 이후 Anonymous Page, Memory Mapped Flie 파트에서 순차적으로 다룬다.

🤨 Supplemantal Page Table

현재 가상 메모리와 물리 메모리를 매핑하는 pml4라는 페이지 테이블을 가지고 있지만, Page Fault 처리를 위해 추가적인 정보를 담고 있는 supplemental table을 구현해야한다.

/* Initialize new supplemental page table */
void supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
	hash_init(&spt -> vm, supplement_hash_func, supplement_less_func, NULL);
}
uint64_t supplement_hash_func ( const struct hash_elem *e, void *aux) {
  /* `hash_entry()로 element에 대한 vm_entry 구조체 검색 */
  struct page *p = hash_entry(e, struct page, elem);
  /* hash_int()를 이용해서 vm_entry의 멤버 vaddr에 대한 해시값을 구하고 반환 */
  return hash_bytes(&p -> va, sizeof p->va); 
}

bool supplement_less_func ( const struct hash_elem *a, const struct hash_elem *b) {
  /* hash_entry()로 각각의 element에 대한 vm_entry 구조체를 얻은 후  vaddr 비교(b가 크다면 True, a가 크다면 False) */
  struct page *p_a = hash_entry(a, struct page, elem);
  struct page *p_b = hash_entry(b, struct page, elem);
  return p_a -> va < p_b -> va;
}
/* Find VA from spt and return page. On error, return NULL. */
struct page * spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
	struct page *page = (struct page *)malloc(sizeof(struct page));

	page -> va = pg_round_down(va);

	
	struct hash_elem *e = hash_find(&spt -> vm, &page -> elem);

	return e != NULL ? hash_entry(e, struct page, elem) : NULL;
}

/* Insert PAGE into spt with validation. */
bool spt_insert_page (struct supplemental_page_table *spt UNUSED,
		struct page *page UNUSED) {
	return hash_insert(&spt -> vm, &page -> elem) == NULL ? true : false; // hase_insert의 결과 값이 NULL인 경우 true
}

그리고 이후 해시 테이블을 사용할 때 필요한 두 함수도 일부 수정을 해주었다.

Frame Management

현재 PintOS에서 사용하는 페이지는 페이지가 할당 되었을 때, 할당된 메모리에 대한 정보(메타 정보: 페이지의 타입, 위치, 권한 등)을 포함하지 않고 있다. include/vm/vm.h 에 있는 struct frame 를 활용하여 물리 메모리 프레임을 관리하고, 이후 각 페이지들이 프레임과 매핑될 수 있는 함수들을 만들어야한다.

  • static struct frame *vm_get_frame (void) → 메모리 풀에서 새로운 물리 메모리로 쓸 페이지를 가져온다.
/* palloc() and get frame. If there is no available page, evict the page
 * and return it. This always return valid address. That is, if the user pool
 * memory is full, this function evicts the frame to get the available memory
 * space.*/
static struct frame *vm_get_frame (void) {
	struct frame *frame = NULL;
	
	/* 메모리 풀은 유저 풀과 커널 풀로 반씩 공간을 차지하고 있다.
	 * 무분별하게 페이지를 할당받을 경우 커널 풀의 페이지를 할당 받을 수 도 있다.
	 * 이런 경우, Page Fault가 발생했을 때 커널 풀의 페이지가 swap out 되는 경우가 발생할 수 있다.
	 * 이를 방지하기 위해서 palloc_get_page의 인자로 PAL_USER를 받아서
	 * 사용자 풀에 한하여 페이지를 할당 받을 수 있도록 한다. 
	 */
	void *kernel_page = palloc_get_page(PAL_USER); // 사용자 풀에서 페이지를 얻어온다.

	if (kernel_page == NULL) {
		PANIC("TODO:");
	}

	struct frame *f = malloc(sizeof(struct frame)); // 프레임 정보를 저장할 메모리 공간을 동적 할당한다.

	if (f == NULL) {
		palloc_free_page(kernel_page);
		PANIC("TODO:");
	}

	// frame관련 기본 정보를 설정
	frame -> kva = kernel_page;
	frame -> page = NULL;

	ASSERT (frame != NULL);
	ASSERT (frame->page == NULL);
	return frame;
}
  • static bool vm_do_claim_page (struct page *page) → 인자로 주어진 page에 물리 메모리 프레임을 매핑
/* 
 * 구체적으로, vm_do_claim_page 함수는 다음과 같은 작업을 수행합니다:
 * 1. 물리 프레임 할당: vm_get_frame 함수를 호출하여 물리 프레임을 할당받습니다.
 * 2. 페이지와 프레임 연결: 페이지 구조체와 할당받은 프레임을 연결합니다.
 * 3. 페이지 테이블 설정: 가상 주소와 물리 주소의 매핑 정보를 페이지 테이블에 추가합니다.
 */
static bool vm_do_claim_page (struct page *page) {
	/* vm_get_frame 함수를 호출함으로써 프레임 하나를 얻습니다. */
	struct frame *frame = vm_get_frame ();

	/* page와 frame을 연결한다. */
	frame->page = page;
	page->frame = frame;

	struct thread *t = thread_current();
	/* MMU를 세팅해야하는데, 이는 가상 주소와 물리 주소를 매핑한 정보를 페이지 테이블에 추가해야 한다는 것을 의미합니다. */
	/* 해당 주소에 페이지를 매핑합니다. */
	pml4_set_page(t->pml4, page -> va, frame -> kva, page -> writable);

	return swap_in (page, frame->kva);
}
  • bool vm_claim_page (void *va UNUSED) → 인자로 받은 va를 페이지에 할당하고, 해당 페이지를 프레임에 매핑(vm_do_claim_page)한다.
/* Claim the page that allocate on VA. */
bool vm_claim_page (void *va UNUSED) {
	struct page *page = spt_find_page(&thread_current() -> spt, va);

	if (page == NULL)
		return false;

	return vm_do_claim_page (page); // 해당 페이지에 프레임 할당
}

🤨 Anonymous Page

디스크에 백업 파일이 없는 페이지, 이름이 없는 페이지를 의미하는 Anonymouse Page를 구현해야한다. Anonymous 페이지는 실행 가능한 파일에서 스택, 힙 영역에서 사용된다.

페이지는 Anonymous Page, File-Backed Page로 나뉘는데, 파일에 의해 매핑된 페이지를 File-Backed File이라고 하며, 매핑되지 않고 컨널로 부터 임의로 할당된 페이지를 Anonymous Page라고 한다.

디스크에 있는 파일이라고 코드와 데이터는 File-Backed Page로 로드 되지만 실행될 때, Stack과 Heap에는 메모리로 할당되게 된다.

Page Initialization with Lazy Loading

지연 로딩은 필요 시점까지 메모리에 로딩을 지연시키는 것을 의미한다. 지연 로딩을 사용하는 경우 페이지는 페이지의 구조체는 존재하지만 매핑된 프레임이 아직 존재하지 않고, 페이지의 콘텐츠도 로드되지 않은 상태를 의미한다. Page Fault가 발생해 페이지의 콘텐츠가 필요할 때 비로소 로드된다.

커널에서 페이지 요청을 받으면, vm_alloc_page_with_initializer 가 호출된다. 인자로 전달받는 타입에 따라 다르게 초기화 함수와 초기화 함수 실행에 필요한 인자를 세팅하여 페이지를 초기화한다. 이후 유저 프로그램으로 제어권을 넘긴다.

이후 로드되지 않은 페이지에 접근하게 되는 경우, Page Fault가 발생하고 이 Page Fault를 처리하는 과정에서 uninit_initialize 를 호출하고, 이때 앞서 설정해두었던 초기화 함수를 호출하여 페이지에 컨텐츠를 채우게 된다.

Lazy Loading for Executable

페이지는 초기에 모두 VM_UNINIT 타입으로 만들어진다. 코드의 uninit_page 구조체로 초기화 되지 않은 페이지를 위한 이후 초기화에 필요한 정보등를 가지고 있다.

// include/vm/uninit.c
/* Uninitlialized page. The type for implementing the
 * "Lazy loading". */
struct uninit_page {
	vm_initializer *init;
	enum vm_type type;
	void *aux;
	bool (*page_initializer) (struct page *, enum vm_type, void *kva); /* Initiate the struct page and maps the pa to the va */
};

Page Fault가 일어났을때, 일단 vm_try_handle_fault 를 실행하여 현재 page fault의 상태를 확인한다.

Page Fault 유형

  1. 소프트웨어 Page Fault - 프로그램이 실행될 때 요청한 페이지가 현재 메모리에 없는 경우 발생. 이 경우 운영체제가 페이지를 디스크에서 메모리로 가져옴. (gitbook 기준 bogus fault)
  2. 하드웨어 Page Fault - 메모리 자체의 문제로 발생해. 예를 들어, 메모리 모듈의 손상이나 잘못된 연결로 인한 문제 등이 여기에 해당된다.

bogus fault인 경우, 앞서 lazy loading을 기다리고 있던 페이지라고 생각하여 페이지의 컨텐츠를 로드해서 유저 프로그램에 반환한다.

  • bool vm_alloc_page_with_initializer (enum vm_type type, void *va, bool writable, vm_initializer *init, void *aux);
    - 페이지 구조체를 할당하고 페이지 타입에 맞는 적절한 초기화 함수를 세팅.
    - 페이지 구조체 초기화
    - 페이지 구조체를 supplement page table에 삽입
/* Create the pending page object with initializer. If you want to create a
* page, do not create it directly and make it through this function or
* `vm_alloc_page`. */
bool 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;

	/* 메모리 주소에 이미 페이지가 있는지 확인 */
	if (spt_find_page (spt, upage) != NULL) {
		return false;
	}

	/*  페이지 구조체 생성 및 초기화 */
	struct page *new_page = (struct page *)malloc(sizeof(struct page));

	if (new_page == NULL)
		return false;

	switch (VM_TYPE(type)) {
		case VM_ANON:
			uninit_new(new_page, upage, init, type, aux, anon_initializer);
			break;
		case VM_FILE:
			uninit_new(new_page, upage, init, type, aux, file_backed_initializer);
			break;
	}
	
	// uninit_new 내부에 writable 관련 항목이 없기 때문에 호출 후에 수정
	new_page -> writable = writable;

	/* supplement page table에 삽입 */
	if(!spt_insert_page(spt, new_page)) {
		free(new_page);
		return false;
	}

	return true;

err:
	return false;
}
void
uninit_new (struct page *page, void *va, vm_initializer *init,
		enum vm_type type, void *aux,
		bool (*initializer)(struct page *, enum vm_type, void *)) {
	ASSERT (page != NULL);

	*page = (struct page) {
		.operations = &uninit_ops,
		.va = va,
		.frame = NULL, /* no frame for now */
		.uninit = (struct uninit_page) {
			.init = init,
			.type = type,
			.aux = aux,
			.page_initializer = initializer,
		}
	};
}

uninit_new 함수를 통해 페이지 초기화 데이터/함수를 가지고 있는 uninit page를 만든다.

  • static bool uninit_initialize (struct page *page, void *kva);
    • Page Fault가 발생했을 때 페이지 초기화 단계에서 세팅했던 initializer를 호출하여 페이지를 초기화한다.
/* 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 */
	vm_initializer *init = uninit->init;
	void *aux = uninit->aux;

	/* TODO: You may need to fix this function. */
	return uninit->page_initializer (page, uninit->type, kva) &&
		(init ? init (page, aux) : true);
}

실행파일의 세그먼트를 로드하는 부분의 구현이 필요하다. 이 함수들은 Lazy Loading에서 사용되고, page fault가 발생했을 때 실행된다. 로드 단계에서 컨텐츠를 페이지 단위로 로드하면서 앞서 만들었던 vm_alloc_page_with_initializer 를 호출한다. 이때 Page Fault가 발생한다.

  • static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable); → 초기 파일 데이터를 페이징하는 단계에서 uninit 페이지를 적재하는 과정으로, 이후 Lazy Loading 될 때, aux 인자를 활용하여 초기화 할 수 있도록 각 페이지의 aux인자에 페이지 초기화에 사용할 데이터를 적재한다.
    • 읽은 바이트 수와 0으로 채울 바이트 수를 계산
    • 로드에 사용할 데이터를 aux에 전달하기 위해 새로운 구조체 (struct aux_struct )에 데이터를 적재
    • vm_alloc_page_with_initializer 를 호출하여 페이지를 할당 및 초기화 → 해당 페이지는 이후 page fault가 발생했을때 lazy_load_segment 함수와 함수에 사용할 인자 aux 를 통해 페이지 컨텐츠를 Lazy Loading
    • 처리한 바이트 수, 0 바이트 수, 시작 주소를 갱신하고 반복
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; // 파일 크기와 읽을 사이즈를 비교하여 실제 읽을 파일 사이즈를 결정 (최대 PGSIZE)
		size_t page_zero_bytes = PGSIZE - page_read_bytes; // 0으로 채울 나머지 부분 크기 계산

		struct aux_struct *aux = (struct aux_struct *)malloc(sizeof(struct aux_struct)); // aux( lazy_load_segment를 위한 데이터 )를 저장할 공간을 동적할당.
		if (aux == NULL) {
			return false;
		}

		// aux 데이터 저장
		aux -> file = file;
		aux -> offset = ofs;
		aux -> read_bytes = page_read_bytes;
		aux -> zero_bytes = page_zero_bytes;

		if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable, lazy_load_segment, aux)) // 페이지를 할당하고 초기화. Page Fault발생시 lazy_load_segment가 호출
			return false;

		/* 다음 페이지 처리를 위해 바이트 수, 시작 주소 갱신 */
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
		ofs += page_read_bytes;
	}
	return true;
}
  • static bool lazy_load_segment (struct page *page, void *aux) → Page Fault가 발생했을 때, 이전에 uninit page를 초기화 할 때 적재했던 데이터를 바탕으로 실제 페이지 컨텐츠를 로드하는 함수
    • 세그먼트를 읽을 파일을 찾고 최종적으로는 세그먼트를 메모리에서 읽음
static bool lazy_load_segment(struct page *page, void *aux)
{
	struct aux_struct *aux_info = (struct aux_struct *)aux; // aux로 페이지 할당을 위한 정보를 가져옴
	
	struct file *file = aux_info -> file;
	off_t offset = aux_info -> offset;
	size_t read_bytes = aux_info -> read_bytes;
	size_t zero_bytes = aux_info -> zero_bytes;

	// 커널 가상 주소
	void *kva = page -> frame -> kva;

	// 파일 데이터 읽기
	if (file_read_at(file, kva, read_bytes, offset) != (int) read_bytes) {
		palloc_free_page(page -> frame -> kva);
		return false;
	}

	memset(kva + read_bytes, 0, zero_bytes); // 나머지 부분 0으로 초기화

	return true;
}
  • static bool **setup_stack**(struct intr_frame *if_) → 사용자 프로그램이 실행될 때 사용할 스택을 초기화 하고 설정 → 프로그램 실행시 필요한 함수, 변수에 대한 정보를 미리 Process 메모리의 Stack 영역에 Lazy Loading 없이 바로 Load한다.
    • 스텍 페이지를 할당하고 스택 포인터를 설정

    • 스택에 삽입한 페이지를 다른 페이지와 구별할 수 있도록 VM_MARKER_0 을 활용하여 표기

      static bool setup_stack(struct intr_frame *if_)
      {
      	bool success = false;
      	void *stack_bottom = (void *)(((uint8_t *)USER_STACK) - PGSIZE);
      
      	if (vm_alloc_page(VM_ANON | VM_MARKER_0, stack_bottom, 1)) {
      		success = vm_claim_page(stack_bottom); // stak bottom의 주소에 페이지를 할당하고 프레임과 매핑
      		 
      		if (success)
      			if_ -> rsp = USER_STACK; // 이후 프로세스에서 사용하기 위해 stack 주소 초기화
      		
      	}
      
      	return success;
      }
  • bool **vm_try_handle_fault** (struct intr_frame *f UNUSED, void *addr UNUSED, bool user UNUSED, bool write UNUSED, bool not_present UNUSED) → 페이지 폴트 원인을 파악하여, Page Fault에 해당하는 페이지를 가지고 Supplemental Page Table를 참조하여 대응하는 물리 가상 프레임을 찾고, 해당 프레임을 통해 Page의 컨텐츠를 Load하는 함수
    • Page Fault 원인을 검사

    • Supplemetal Page Table 참조

    • Page Fault 대상 page 컨텐츠 load

    • 페이지 테이블 갱신

      /* Return true on success */
      bool vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED,
      		bool user UNUSED, bool write UNUSED, bool not_present UNUSED) {
      	// addr 주소 유효성 검사
      	if (addr == NULL || is_kernel_vaddr(addr))
      		return false;
      
      	/* Page Fault 원인을 검사 */
      	if ( !not_present )
      		return false;
      	
      	/* Supplemetal Page Table 참조 */
      	struct supplemental_page_table *spt UNUSED = &thread_current ()->spt;
      
      	/* Page Fault 대상 page 컨텐츠 load */
      	struct page *page = spt_find_page(spt, addr);
      	
      	if (page == NULL) {
      		return false;
      	}
      	
      
      	/* write 불가능한 페이지에 접근한 경우 */
      	if (write == 1 && page -> writable == 0) {
      		return false;
      	}
      
      	/* 페이지 테이블 갱신 */
      	return vm_do_claim_page (page);
      }

Supplemental Page Table - Revisit

위의 초기화 관련 함수를 사용한 Supplemental Page Table 관련 함수 이어서 구현

  • supplemental_page_table_copy → src의 supplemental page table을 순회하면서 dst의 supplemental page table에 항목을 복사
    /* 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) {	
    
    	// src의 해시 테이블을 순회하기 위한 구조체 선언
    	struct hash_iterator i;
    
    	// hash의 첫번째 요소를 hash_iterator에 설정
    	hash_first (&i, &src -> vm);
    
    	// hash table 내부를 순회 
    	while (hash_next (&i))
       {
       	struct page *curr_page = hash_entry(hash_cur (&i), struct page, elem);
    		if ( curr_page == NULL )
    			return false;
    
    		enum vm_type type = curr_page -> operations -> type;
    		void *upage = curr_page -> va;
    		bool writable = curr_page -> writable;
    
    		// uninit 타입 페이지는 spt에 페이지만 로드 되어있을 뿐, 관련된 프레임과는 매핑되어 있지 않은 상태
    		// src의 spt entry의 값을 통해 새로운 페이지를 생성
    		if ( type == VM_UNINIT ) {
    			vm_alloc_page_with_initializer(VM_ANON, upage, writable, curr_page -> uninit.init, curr_page -> uninit.aux);
    
    			// uninit 페이지가 생성되었다면 다음 테이블 엔트리 처리
    			continue;
    		}
    
    		// anonymous, file_mapped 타입인 경우 uninit 상태에서 pagefault로 컨텐츠가 로드되었거나, 직접 로드된 상태의 페이지를 의미
    		// 해당 페이지는 lazy loading 없이 직접 페이지를 로드
    		if ( !vm_alloc_page_with_initializer(type, upage, writable, NULL, NULL) )
    			return false;
    
    		// 페이지 로드 후 프레임과 매핑 ( 매핑만 )
    		if ( !vm_claim_page(upage) )
    			return false;
    
    		// dst 해시 테이블 내부에 페이지가 들어있는지 확인
    		struct page *new_p = spt_find_page(dst, upage);
    
    		if (new_p == NULL || new_p->frame == NULL) {
    					return false;
    		}
    
    		// 프레임 내부에 정보를 페이지 한 개만큼 복사
    		memcpy(new_p -> frame -> kva, curr_page -> frame -> kva, PGSIZE);
       }
    
    		return true;
    }
  • supplemental_page_table_kill → supplemental page table에 의해 유지되던 모든 리소스를 해제. 각 페이지의 destroy를 호출하여 페이지를 free
    /* Free the resource hold by the supplemental page table */
    void
    supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED) {
    	/* TODO: Destroy all the supplemental_page_table hold by thread and
    	 * TODO: writeback all the modified contents to the storage. */
    }

Page Cleanup

페이지 타입에 맞는 destroy 함수가 각각 필요하다.

  • static void uninit_destroy (struct page *page)
    • 페이지 구조체의 리소스를 해제한다.
  • static void anon_destroy (struct page *page)
    • 익명 페이지의 리소스를 해제한다.

🤨 Review


💫 Trouble Shooting

Case 1

  • Page Fault하는 페이지가 커널 메모리 풀의 페이지로 추측되는 상황
  • 조건문을 쪼갰을 때 다음과 같이 is_kernel_vaddr 로 체크하는 부분에서 뻑이감
해결
-> va를 저장하는 포인터를 2개 사용하고 있었음.

Case 2

  • fork 하는 과정에서 에러 발생
  • fork 시스템 콜로 자식 프로세스가 생성되는 과정에서 호출된 supplemental_page_table_copy 함수에서 exit(-1)로 비정상적으로 프로그램이 종료되는 현상
...
    
enum vm_type type = curr_page -> operations -> type;
    
...

-> 페이지 타입을 의미하는 멤버 변수를 잘못 사용. 현재 페이지의 타입을 사용해야함.


🚀 Result

    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
    **FAIL** tests/vm/pt-grow-stack
    pass tests/vm/pt-grow-bad
    **FAIL** tests/vm/pt-big-stk-obj
    pass tests/vm/pt-bad-addr
    pass tests/vm/pt-bad-read
    pass tests/vm/pt-write-code
    **FAIL** tests/vm/pt-write-code2
    **FAIL** 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
    **FAIL** tests/vm/mmap-read
    **FAIL** tests/vm/mmap-close
    **FAIL** tests/vm/mmap-unmap
    **FAIL** tests/vm/mmap-overlap
    **FAIL** tests/vm/mmap-twice
    **FAIL** tests/vm/mmap-write
    pass tests/vm/mmap-ro
    **FAIL** tests/vm/mmap-exit
    **FAIL** tests/vm/mmap-shuffle
    **FAIL** tests/vm/mmap-bad-fd
    **FAIL** tests/vm/mmap-clean
    **FAIL** tests/vm/mmap-inherit
    **FAIL** tests/vm/mmap-misalign
    **FAIL** tests/vm/mmap-null
    **FAIL** tests/vm/mmap-over-code
    **FAIL** tests/vm/mmap-over-data
    **FAIL** tests/vm/mmap-over-stk
    **FAIL** tests/vm/mmap-remove
    pass tests/vm/mmap-zero
    **FAIL** tests/vm/mmap-bad-fd2
    **FAIL** tests/vm/mmap-bad-fd3
    **FAIL** tests/vm/mmap-zero-len
    **FAIL** tests/vm/mmap-off
    **FAIL** tests/vm/mmap-bad-off
    **FAIL** tests/vm/mmap-kernel
    **FAIL** tests/vm/lazy-file
    pass tests/vm/lazy-anon
    **FAIL** tests/vm/swap-file
    **FAIL** tests/vm/swap-anon
    **FAIL** tests/vm/swap-iter
    **FAIL** 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
    **FAIL** tests/filesys/base/syn-read
    pass tests/filesys/base/syn-remove
    **FAIL** 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
    
    **38** of **141** tests failed.
profile
나도 잘하고 싶다..!

0개의 댓글