이번 포스팅에서는 익명 페이지라고 부르는 디스크 기반이 아닌 이미지를 구현한다.
일명 매핑에는 백업 파일 혹은 장치가 없다. 파일 기반 페이지와 달리 이름이 있는 파일 소스를 가지고 있지 않기 때문에 익명이라고 한다. 익명 페이지는 실행 가능한 파일에서 스택과 힙 영역에서 사용한다.
Lazy Loading
지연 로딩은 필요 시점까지 메모리의 로딩을 지연시키는 방법이다. 페이지가 할당되었다는 것은 대응되는 페이지 구조체는 있지만 연결된 물리 메모리 프레임은 아직 없고 페이지에 대한 실제 콘텐츠들이 아직 로드되지 않았다는 것을 의미한다. 콘텐츠는 페이지 폴트로 인해 실제로 콘텐츠가 필요하다는 시그널을 받을 때 로드된다.
pintos VM에서는 일단 3가지 페이지 타입을 다룬다. 파일 시스템에서 추가적인 페이지가 있는 것 같지만 일단은 VM 기준으로 생각하겠다. 커널이 새로운 페이지 요청을 받으면 vm_alloc_page_with_initializer 가 호출된다. 이 함수는 페이지 구조체를 할당하고 페이지 타입에 맞는 적절한 초기화 함수를 세팅함으로써 새로운 페이지를 초기화한다. 그리고 유저 프로그램을 제어권을 넘긴다. 이 말은 페이지 구조체를 할당했지만 아직 해당 페이지에 프레임을 매핑하지 않고 해당 페이지의 타입과 초기화 함수만 등록한다는 것이다.
이후 유저 프로그램이 실행되면서 지연 로딩으로 인해 콘텐츠가 아직 로드되지 않은 페이지에 접근하면 페이지 폴트가 일어나고 이 페이지 폴트를 처리하는 과정에서 페이지 타입별로 세팅해 둔 초기화 함수가 호출된다.
vm_alloc_page_with_initializer 구현
- vm/vm.c
* 초기화 함수와 함께 대기 중인 페이지 객체를 생성한다. 페이지를 직접 생성하지 말고,
* 반드시 이 함수나 `vm_alloc_page`를 통해 생성하라.
* 페이지를 할당하고 타입을 uninit으로 설정한다.
* */
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;
/* 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));
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;
}
/* TODO: spt에 페이지를 삽입합니다. */
uninit_new(p, upage, init, type, aux, page_initializer);
p->writable = writable;
return spt_insert_page(spt, p);
}
err:
return false;
}
vm_alloc_page_initializer 함수는 지연 초기화 방식으로 사용할 수 있는 가상 페이지를 등록하는 함수다. 직접 page를 생성하거나 초기화하지 말고, 반드시 이 함수를 통해서만 가상 페이지를 생성한다.
- 각 프로세스(스레드) 마다 SPT를 관리하고 있다. 먼저 이 SPT를 가져온다.
- 해당 upage에 이미 페이지가 등록되어 있으면 중복 등록 방지에 의해 실패를 반환한다.
- 타입에 따라 해당 페이지가 어떤 초기화가 필요한지 결정한다.
- 페이지를 UNINIT 타입으로 설정하고 페이지 초기화 로직을 나중으로 미룬다.
- spt에 페이지를 삽입한다.
load_segment 구현
- userprog/process.c
/* Loads a segment starting at offset OFS in FILE at address
* UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual
* memory are initialized, as follows:
*
* - READ_BYTES bytes at UPAGE must be read from FILE
* starting at offset OFS.
*
* - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
*
* The pages initialized by this function must be writable by the
* user process if WRITABLE is true, read-only otherwise.
*
* Return true if successful, false if a memory allocation error
* or disk read error occurs. */
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. */
struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
lazy_load_arg->file = file; // 내용이 담긴 파일 객체
lazy_load_arg->ofs = ofs; // 이 페이지에서 읽기 시작할 위치
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_ANON, upage, writable, lazy_load_segment, lazy_load_arg))
return false;
/* Advance. */
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
upage += PGSIZE;
ofs += page_read_bytes;
}
return true;
}
load_segment는 프로세스 실행 시 실행 파일의 특정 segment (코드, 데이터 등)를 가상 메모리에 매핑하기 위해 사용한다. userprog에서는 미리 구현되어 있던 load_segment 함수가 있었고 그 함수에서 지연 로딩이 아닌 즉시 로딩을 진행했다.
매개변수 | 설명 |
file | 읽을 실행 파일 객체 |
ofs | 읽기 시작할 파일 내 offset |
upage | 매핑할 사용자 가상 주소 시작점 |
read_bytes | 파일에서 읽어야 할 전체 바이트 수 |
zero_bytes | 0으로 초기화할 전체 바이트 수 |
writable | 페이지 쓰기 가능 여부 (코드: 읽기 전용 / 데이터: 쓰기 가능) |
주요 코드 설명
while (read_bytes > 0 || zero_bytes > 0) {
- 남은 segment를 4KB 페이지 단위로 나누어 반복 처리한다.
size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
size_t page_zero_bytes = PGSIZE - page_read_bytes;
- 현재 페이지에서 읽어야 할 파일 데이터는 최대 4KB (page_read_bytes)
- 나머지는 0으로 초기화해야 하는 영역 (page_zero_bytes)
struct lazy_load_arg *lazy_load_arg = malloc(...);
lazy_load_arg->file = file;
lazy_load_arg->ofs = ofs;
lazy_load_arg->read_bytes = page_read_bytes;
lazy_load_arg->zero_bytes = page_zero_bytes;
- 별도의 구조체를 선언했다.
struct lazy_load_arg {
struct file *file;
off_t ofs;
uint32_t read_bytes;
uint32_t zero_bytes;
};
- 이 구조체는 나중에 lazy_load_segment()에서 사용됨
- 페이지가 실제로 메모리에 로드될 때(vm_claim_page() -> swap_in() -> lazy_load_segment() 호출됨) 필요한 정보
if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable, lazy_load_segment, lazy_load_arg))
return false;
- 타입은 VM_ANON으로 지정 (파일 백업 구조를 따르지 않고 메모리 기반)
- 실제로 메모리에 올리지 않고 SPT에 UNINIT 타입 페이지를 등록
- lazy_load_segment()는 나중에 로딩 시점에 실행될 함수
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
upage += PGSIZE;
ofs += page_read_bytes;
- 다음 페이지를 준비하기 위해 가상 주소, 읽기 오프셋 등 갱신을 한다.
lazy_load_segment 구현
vm_alloc_page_with_initialize 함수의 네 번째 인자가 lazy_load_segment이고 이 함수는 실행 가능한 파일의 페이지들을 초기화하는 함수이고 page_fault가 발생할 때 호출된다. 이 함수는 구조체와 aux를 인자로 받는다.
- userprog/process.c
* aux를 인자로 받는데 이 aux 위에 정의된 lazy_load_arg를 받는다.
* 이 lazy_load_arg는 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. */
struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)aux;
// 1) 파일의 position을 ofs으로 지정한다.
file_seek(lazy_load_arg->file, lazy_load_arg->ofs);
// 2) 파일을 read_bytes만큼 물리 프레임에 읽어 들인다.
if (file_read(lazy_load_arg->file, page->frame->kva, lazy_load_arg->read_bytes) != (int)(lazy_load_arg->read_bytes))
{
palloc_free_page(page->frame->kva);
return false;
}
// 3) 다 읽은 지점부터 zero_bytes만큼 0으로 채운다.
memset(page->frame->kva + lazy_load_arg->read_bytes, 0, lazy_load_arg->zero_bytes);
return true;
}
lazy_load_segment() 는 지연로딩을 구현할 핵심 함수로 해당 가상 페이지가 처음 접근될 때 파일에서 데이터를 읽어 물리 메모리에 올리고 나머지 부분을 0으로 초기화한다.
주요 코드 설명
struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)aux;
- load_segment() 에서 생성한 구조체
struct lazy_load_arg {
struct file *file;
off_t ofs;
size_t read_bytes;
size_t zero_bytes;
};
- 이 구조체에 해당 페이지에서 수행할 작업의 모든 정보가 들어 있다.
file_seek(lazy_load_arg->file, lazy_load_arg->ofs);
- 파일 포인터를 현재 페이지에 맞는 offset으로 이동한다.
if (file_read(lazy_load_arg->file, page->frame->kva, lazy_load_arg->read_bytes) != (int)(lazy_load_arg->read_bytes)) {
palloc_free_page(page->frame->kva);
return false;
}
- page->frame->kva는 이미 vm_get_frame()을 통해 확보된 물리 프레임의 주소
- 파일에서 데이터를 읽는데 실패하면 → 프레임을 해제하고 실패 반환
memset(page->frame->kva + lazy_load_arg->read_bytes, 0, lazy_load_arg->zero_bytes);
- 한 페이지의 나머지 바이트는 반드시 0으로 채워져야 하므로 명시적으로 초기화
- 이는 ELF 로딩 규격에 따라 .bss 영역 등에 해당한다.
setup_stack 구현
- userprog/process.c
/* Create a PAGE of stack at the USER_STACK. Return true on success.
* 스택의 페이지를 생성하는 함수 스택 시작점인 USER_STACK에서 PSIZE 만큼 아래로 내린 지점에 페이지를 생성한다.
*/
static bool
setup_stack (struct intr_frame *if_) {
bool success = false;
void *stack_bottom = (void *) (((uint8_t *) USER_STACK) - PGSIZE);
/* TODO: Map the stack on stack_bottom and claim the page immediately.
* TODO: If success, set the rsp accordingly.
* TODO: You should mark the page is stack. */
/* TODO: Your code goes here */
/* 1) stack_bottom에 페이지를 하나 할당받는다.
* VM_MARKER_0: 스택이 저장된 메모리 페이지임을 식별하기 위해 추가
* writable: argument_stack()에서 값을 넣어야 하니 True */
if (vm_alloc_page_with_initializer(VM_ANON | VM_MARKER_0, stack_bottom, 1, NULL, NULL))
{
// 2) 할당 받은 페이지에 바로 물리 프레임을 매핑한다.
success = vm_claim_page(stack_bottom);
if (success)
// 3) rsp를 변경한다. (argument_stack에서 이 위치부터 인자를 push한다.)
if_->rsp = USER_STACK;
}
return success;
}
스택 할당 부분이 새로운 메모리 관리 시스템에 적합할 수 있도록 setup_stack을 수정해야 한다. 첫 스택 페이지는 지연적으로 할당될 필요가 없다. 당신은 페이지 폴트가 발생하는 것을 기다릴 필요 없이 스택 페이지를 load time 때 커맨드 라인의 인자들과 함께 할당하고 초기화할 수 있다.
- 사용자 프로세스를 실행하려면 스택이 있어야 한다. 이 함수는 유저 가상 주소 공간의 최상단 USER_STACK에 스택용 페이지를 할당하고 설정한다.
void *stack_bottom = (void *) (((uint8_t *) USER_STACK) - PGSIZE);
- 유저 스택은 아래 방향으로 성장하므로, 초기 스택 페이지는 USER_STACK - PGSIZE부터 시작
- 이 위치에 페이지를 할당하고, 스택 포인터(rsp)는 여전히 USER_STACK에 둠
vm_alloc_page_with_initializer(VM_ANON | VM_MARKER_0, stack_bottom, true, NULL, NULL)
- VM_ANON: 익명 페이지 (파일이 아닌 메모리 기반)
- VM_MARKER_0: 스택임을 표시 (추후 grow_stack() 판단 시 사용 가능)
- writable = true: 사용자 인자 등을 push할 수 있어야 하므로 쓰기 가능해야 함
- init = NULL: lazy load 아님 → 직접 즉시 메모리에 올릴 예정
- 성공 시 SPT에 등록만 된 상태 (아직 프레임 매핑 전)
success = vm_claim_page(stack_bottom);
- 실제 물리 메모리(프레임)를 할당받고, 해당 가상 주소에 매핑
- 실패 시 success = false 반환
if (success)
if_->rsp = USER_STACK;
- 현재 유저 프로그램의 스택 포인터를 USER_STACK으로 설정
- 이 위치부터 argument 값을 stack에 push할 수 있음 (보통 argument_stack()에서 push 시작)
vm_try_handle_fault 구현
- vm/vm.c
/* Return true on success
* 페이지 폴트 핸들러 - 페이지 폴트 발생시 제어권을 전달 받는다.
* 물리 프레임이 존재하지 않아서 발생한 예외는 not_present 가 true다
* 그 경우 물리 프레임 할당을 요청하는 vm_do_claim_page를 호출한다.
* 반대로 not_present 가 false인 경우는 물리 프레임이 할당되어 있지만 폴트가 발생한 것이다.
* read-only page에 write를 한경우 등 이 때에는 예외 처리를 하면 된다.
* 그렇다고 해서 not_present가 true인 경우에서 read-only page에 요청을 할 수 있으니 이에
* 대한 예외를 처리하라
*/
bool
vm_try_handle_fault (struct intr_frame *f UNUSED, void *addr UNUSED,
bool user UNUSED, bool write UNUSED, bool not_present UNUSED)
{
struct supplemental_page_table *spt UNUSED = &thread_current ()->spt;
struct page *page = NULL;
/* TODO: Validate the fault */
/* TODO: Your code goes here */
// 1. 주소 유효성 검사
if (addr == NULL)
return false;
if (is_kernel_vaddr(addr))
return false;
if (not_present) // 접근한 메모리의 physical page가 존재하지 않은 경우
{
/* TODO: Validate the fault */
page = spt_find_page(spt, addr);
if (page == NULL)
return false;
if (write == 1 && page->writable == 0) // write 불가능한 페이지에 write 요청한 경우
return false;
return vm_do_claim_page(page);
}
return false;
}
vm_try_handle_fault() 는 페이지 폴트 핸들러이다. 사용자 프로그램 실행 중 페이지 폴트가 발생했을 때, 해당 폴트를 처리할 수 있는지 확인하고 가능하다면 가상 주소에 대한 물리 메모리 매핑을 수행하는 함수이다.
매개변수 | 설명 |
f | 인터럽트 프레임 (예외 발생 당시의 CPU 상태, rsp, rip 등 포함) |
addr | 접근 시도한 가상 주소 (Fault address) |
user | 유저 모드에서 발생한 예외인지 여부 |
write | 쓰기 접근이었는지 여부 |
not_present | 해당 주소에 물리 메모리 매핑이 없었는지 여부 |
코드 설명
if (addr == NULL || is_kernel_vaddr(addr))
return false;
- 접근한 주소가 NULL이거나 커널 영역 주소라면 즉시 실패
- 사용자가 잘못된 포인터를 참조했을 가능성
if (not_present) {
- 접근한 가상 주소가 아직 물리 프레임에 매핑되지 않은 경우
- 주로 lazy loading, 스택 성장, swap in 상황에서 발생
page = spt_find_page(spt, addr);
if (page == NULL)
return false;
- Supplemental Page Table에서 해당 가상 주소에 대한 struct page 검색
- 없다면 복구할 수 없는 접근 → 실패
if (write == 1 && page->writable == 0) // write 불가능한 페이지에 write 요청한 경우
return false;
- 쓰기 접근인데 해당 페이지가 읽기 전용이라면 → 잘못된 접근 → 실패
return vm_do_claim_page(page);
- vm_do_claim_page()는 vm_get_frame()으로 프레임을 확보하고, swap_in() 및 pml4_set_page()로 MMU에 등록
이 그햔까지 되었다면 VM과 fork, exec, wait? 이런 테스트를 제외하고 통과할 것이다.
User Program - Fork 테스트 통과를 위한 코드 구현
그렇다면 fork를 통과하기 위해 어떤 것을 구현해야 할까? fork는 부모 프로세스를 자식에게 복사를 하는 것이다. 그렇다면 부모의 페이지 정보도 자식에게 복사가 될 것이다. 현재는 아니지만 일단 부모의 페이지 정보를 복사해 별도의 프레임에 매핑해서 복사를 하겠다는 것이 현재 방법이다.
- 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;
}
/* 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;
}
supplemental_page_table_copy 는 프로세스 복사 시 필요한 SPT를 복사하는 함수이다.
struct hash_iterator i;
hash_first(&i, &src->pages);
while (hash_next(&i)) {
...
}
- src->pages는 해시 테이블이므로, hash iterator를 사용해 전체 페이지를 순회
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;
- 현재 순회 중인 원본 페이지 정보 가져오기
- 가상 주소, 페이지 타입, 쓰기 가능 여부 저장
if (type == VM_UNINIT) {
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;
}
- lazy load 중인 페이지
- 실제 데이터는 아직 메모리에 없고, 최초 접근 시 init 함수 호출 예정
- → 복사할 때도 동일하게 init, aux 정보만 복제하고 내용은 복사하지 않음
if (!vm_alloc_page_with_initializer(type, upage, writable, NULL, NULL))
return false;
if (!vm_claim_page(upage))
return false;
- type에 따라 페이지를 dst에 등록하고
- 바로 메모리에 프레임까지 연결 (vm_claim_page)
- lazy-load가 아닌 경우이므로 직접 데이터 복사가 필요
struct page *dst_page = spt_find_page(dst, upage);
memcpy(dst_page->frame->kva, src_page->frame->kva, PGSIZE);
- 실제로 사용자 데이터(코드, 데이터 등)를 복사
- src_page의 프레임 내용을 dst_page의 프레임에 그대로 복사
supplemental_page_table_kill
- vm/vm.c
/* Free the resource hold by the supplemental page table */
void
supplemental_page_table_kill (struct supplemental_page_table *spt UNUSED)
{
/* TODO: 스레드가 보유한 모든 supplemental_page_table을 제거하고,
* TODO: 수정된 내용을 스토리지에 기록(writeback)하세요. */
hash_clear(&spt->pages, hash_page_destroy);
}
supplemental_page_table_kill 함수는 SPT를 해제하는 함수이다. 프로세스 종료 시, 해당 프로세스가 보유한 모든 가상 페이지에 대한 정보와 자원을 해제한다.
함수는 간단하다.
- spt->pages: SPT의 핵심 해시 테이블 (struct hash)
- hash_clear(...): 해당 해시 테이블을 전부 제거하고, 각 원소 제거 시 호출할 cleanup 함수로 hash_page_destroy() 지정
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
FAIL 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
FAIL tests/userprog/fork-read
FAIL 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
FAIL 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
FAIL 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
FAIL tests/vm/mmap-ro
FAIL tests/vm/mmap-exit
FAIL tests/vm/mmap-shuffle
pass 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
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass 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
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
37 of 141 tests failed.
'크래프톤 정글' 카테고리의 다른 글
[pintos] Week4~5: Virtual Memory - Part.5 Memory Mapped Files (0) | 2025.06.07 |
---|---|
[pintos] Week4~5: Virtual Memory - Part.4 Stack Growth (0) | 2025.06.06 |
[pintos] Week4~5: Virtual Memory - Part.2 Memory Management (1) | 2025.06.06 |
[pintos] Week4~5: Virtual Memory - Part.1 가상 메모리란? (0) | 2025.06.06 |
[pintos] Week2~3: User Program 외전 - Linked List를 이용한 FD관리 (0) | 2025.05.26 |