※ 100% 완벽한 코드는 아닙니다! 참고용으로 봐주세요. ※
Memory Management
저희는 Gitbook에 따라 먼저 spt 테이블을 구현합니다. 테이블을 구현하는 방식 중에는 배열, 리스트, 비트맵, 해시 테이블 등이 있는데 저같은 경우 해시 테이블을 채택했습니다.
첫번째로, spt를 사용하려면 초기화가 필수적이므로 초기화하는 코드를 먼저 작성해보았습니다.
그 과정에서 page_hash와 page_less 함수를 추가적으로 구현했습니다. 각 함수는 다음과 같은 역할을 합니다.
page_hash: 페이지의 가상주소를 바탕으로 고유한 해시값을 생성합니다.
// 페이지 va를 바탕으로 고유한 해시값 생성
// 해시값을 통해 bucket을 찾는다.
unsigned page_hash(const struct hash_elem *e, void *aux UNUSED) {
struct page *p = hash_entry(e, struct page, hash_elem);
return hash_bytes(&p -> va, sizeof(p -> va)); // 주소를 기준으로 비교
}
page_less: 해시값이 같아서 충돌될 시 어떤 주소가 작은 가에 대해 비교하는 정렬기준을 제공합니다.
// bucket 내부 충돌 일시(같은 해시값) 정렬기준을 제공합니다.
// 내부적인 bucket에서 이진 탐색트리 또는 리스트 정렬할 때 사용된다.
bool page_less(const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED) {
struct page *page_a = hash_entry(a, struct page, hash_elem);
struct page *page_b = hash_entry(b, struct page, hash_elem);
return page_a -> va < page_b -> va; // 주소를 기준으로 비교
}
위의 두 함수를 사용하여 spt 테이블을 초기화합니다. 그 과정에서 hash_inti을 사용합니다.
void supplemental_page_table_init (struct supplemental_page_table *spt) {
hash_init(&spt->spt_hash, page_hash, page_less, NULL);
}
vm.h
vm.h에 구조체 작업을 먼저 설명해보겠습니다. 추후에도 쓰이니, 미리 작성하는 것도 좋습니다.
먼저 spt를 위해 hash 구조체를 인클루드 해주고, lock을 위한 frame_table
구조체 두개를 선언해 줍니다.
/* 프로젝트3 */
#include "lib/kernel/hash.h" // spt를 위한 hash 구조체 인클루드
/* 프로젝트3 */
struct page_operations;
struct thread;
/* 프로젝트3 */
static struct list frame_table; // 전역 frame 리스트(vm_get_frame에서 사용)
static struct lock frame_table_lock; // 멀티쓰레드 환경 보호용
/* 프로젝트3 */
page 구조체에도 3개의 구조체를 선언해줍니다.
struct page {
const struct page_operations *operations;
void *va; /* Address in terms of user space */
struct frame *frame; /* Back reference for frame */
/* 프로젝트3 */
struct hash_elem hash_elem; // spt 테이블 초기화시 사용
bool is_stack;
bool writable;
/* 프로젝트3 */
frame 구조체
프레임에도 frmae table
에 넣기위한 요소를 추가해줍니다.
/* The representation of "frame" */
struct frame {
/* 프로젝트3 */
void *kva; // 커널의 가상 주소
struct page *page; // 이 프레임에 매핑된 페이지 (처음은 NULL)
struct list_elem elem; // frame table에 넣기 위한 요소
/* 프로젝트3 */
};
supplemental_page_table 구조체
supplemental_page_table
도 맨처음에는 비어있는데 초기화를 위한 spt_hash 구조체를 선언하기 위해 구조체를 추가합니다.
// 현재 프로세스의 메모리 공간을 나타냅니다.
// 이 구조체에 대해 특정 디자인을 따르도록 강요하고 싶지 않습니다.
// 모든 디자인은 사용자에게 달려 있습니다.
struct supplemental_page_table {
/* 프로젝트3 */
struct hash spt_hash; // 초기화를 위한 spt_hash 구조체 선언
/* 프로젝트3 */
};
이제 git book의 순서에 따라 요구하는 사항을 구현해줍니다.
vm.c
supplemental_page_table_init
보충 페이지 테이블을 초기화해줍니다. 위에서 선언해준 spt_hash
구조체를 사용합니다.
// 새로운 보충 페이지 테이블을 초기화합니다.
// spt는 페이지 폴트 발생 시 해당 vm에서 어떤 데이터가 있어야 하는지 파악합니다.
void
supplemental_page_table_init (struct supplemental_page_table *spt UNUSED) {
hash_init(&spt -> spt_hash, page_hash, page_less, NULL);
}
해당 과정에서 page_hash라는 해시값을 통해 bucket을 찾는 함수와 page_less라는 bucket 내부 충돌 시의 정렬 기준을 제공하는 함수가 필요합니다.
// 페이지 va를 바탕으로 고유한 해시값 생성
// 해시값을 통해 bucket을 찾는다.
uint64_t page_hash(const struct hash_elem *e, void *aux) {
struct page *p = hash_entry(e, struct page, hash_elem);
return hash_bytes(&p -> va, sizeof(p -> va)); // 주소를 기준으로 비교
}
// bucket 내부 충돌 일시(같은 해시값) 정렬기준을 제공합니다.
// 내부적인 bucket에서 이진 탐색트리 또는 리스트 정렬할 때 사용된다.
bool page_less(const struct hash_elem *a, const struct hash_elem *b, void *aux) {
struct page *page_a = hash_entry(a, struct page, hash_elem);
struct page *page_b = hash_entry(b, struct page, hash_elem);
return page_a -> va < page_b -> va; // 주소를 기준으로 비교
}
→ 해당 함수는 supplemental_page_table_init
보다 위에 선언되어야합니다. 저는 그래서 페이지 상단에 프로토타입도 선언해주었습니다.
uint64_t page_hash(const struct hash_elem *e, void *aux UNUSED);
bool page_less(const struct hash_elem *a, const struct hash_elem *b, void *aux UNUSED);
spt_find_page
주어진 정보에 따라 spt에서 가상주소를 찾아서 페이지를 반환하고 오류가 발생한다면 NULL을 반환하게끔 설계했습니다.
/* Find VA from spt and return page. On error, return NULL. */
// spt에서 VA를 찾아 페이지를 반환합니다. 오류가 발생하면 NULL을 반환합니다.
struct page *
spt_find_page (struct supplemental_page_table *spt UNUSED, void *va UNUSED) {
struct page *page = NULL;
struct page dummy;
dummy.va = pg_round_down(va); // 시작 주소를 맞춰준다. (정렬)
/* TODO: Fill this function. */
// 기능을 구현하세요.
struct hash_elem *e = hash_find(&spt -> spt_hash, &dummy.hash_elem);
if (e == NULL) return NULL;
if(e != NULL)
page = hash_entry(e, struct page, hash_elem);
return page;
}
spt_insert_page
spt에 가상 주소가 존재하는지, 아닌지 확인 후에 NULL이라면 spt에 페이지를 삽입합니다.
// 검증을 통해 spt에 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;
}
vm_get_frame
palloc_get_page를 호출해서 사용자 풀에서 새 물리 페이지를 가져옵니다.
만약, 페이지를 성공적으로 가져왔으면 프레임을 초기화 후 반환해줍니다. 실패시에는 PANIC(”todo”)를 통해 커널 패닉을 시킵니다.
저같은 경우에는 나중을 위한 lock까지 구현을 해두었습니다.
// palloc() 함수는 프레임을 가져옵니다. 사용 가능한 페이지가 없으면 해당 페이지를 제거하고 반환합니다.
// 이 함수는 항상 유효한 주소를 반환합니다.
// 즉, 사용자 풀 메모리가 가득 차면 이 함수는 프레임을 제거하여 사용 가능한 메모리 공간을 가져옵니다.
static struct frame *
vm_get_frame (void) {
struct frame *frame = NULL;
/* TODO: Fill this function. */
frame = calloc(1, sizeof(struct frame));
if(frame == NULL) PANIC("todo");
void *kva = palloc_get_page(PAL_USER); // 커널이 접근할 수 있는 물리 페이지 주소
if(kva == NULL) PANIC("todo"); // 사용자 풀에서 페이지를 성공적으로 가져왔는가?
frame -> kva = kva;
lock_acquire(&frame_table_lock);
list_push_back(&frame_table, &frame->elem);
lock_release(&frame_table_lock);
ASSERT (frame != NULL);
ASSERT (frame->page == NULL);
return frame;
}
vm_do_claim_page
먼저 vm_get_frame
을 통해 프레임을 얻습니다. 이후에 MMU를 설정해줍니다.pml4_set_page
함수를 통해 가상 주소에서 페이지 테이블의 물리적 주소로 매핑을 추가합니다. bool값을 통해 작업의 성공 여부를 반환하여야 합니다.
// PAGE를 선언하고 mmu를 설정하세요.
static bool
vm_do_claim_page (struct page *page) {
struct frame *frame = vm_get_frame ();
if(frame == NULL) {
return false;
}
/* Set links */
frame->page = page;
page->frame = frame;
/* TODO: Insert page table entry to map page's VA to frame's PA. */
// 페이지의 VA를 프레임의 PA에 매핑하기 위해 페이지 테이블 항목을 삽입합니다.
pml4_set_page(thread_current() -> pml4, page -> va, frame -> kva, page->writable);
return swap_in (page, frame->kva);
}
vm_claim_page
spt_find_page
함수를 통해 페이지를 가져온 다음, 해당 페이지와 함께 vm_do_claim_page
함수를 호출하여 할당할 페이지를 선언해줍니다.
// VA로 할당된 페이지를 선언합니다.
bool
vm_claim_page (void *va UNUSED) {
struct page *page = NULL;
/* TODO: Fill this function */
// 기능을 구현하세요.
page = spt_find_page(&thread_current() -> spt, va); // spt 페이지를 가져옵니다.
return vm_do_claim_page (page);
}
Anonymous Page
익명 페이지는 실행 파일에서 스택이나 힙과 같은 용도로 사용됩니다. 지연로딩이라는 필요한 시점에 메모리를 로드하는 기술에 필요합니다. 자세한 내용은 Gitbook을 참고하세요.
vm.c
vm_alloc_page_with_initializer
주어진 타입으로 초기화되지 않은 페이지를 생성하면됩니다.spt_find_page
를 통해 NULL인지 확인하면, 페이지를 생성 → VM_TYPE에 따라 초기화 파일을 가져옴 → uninit_new
함수를 통해 필드를 수정합니다.
완료되었다면 해당 페이지를 spt_insert_page
함수를 통해 spt에 삽입합니다.
특히나 중간의 page -> writable = writable;
와 같은 코드를 빠뜨리면 안됩니다.
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) {
// 해야될 것: 페이지를 생성하고, VM 유형에 따라 초기화 파일을 가져온 후, uninit_new를 호출하여 "uninit" 페이지 구조체를 생성합니다.
// 해야될 것: uninit_new를 호출한 후 필드를 수정해야 합니다.
// 해당 함수는 아직 메모리에 로딩되지 않은 페이지에 대한 메타데이터를 생성해서 SPT에 등록하는게 목적입니다.
struct page *page = malloc(sizeof(struct page)); // page 할당
if (page == NULL)
return false;
enum vm_type real_type = VM_TYPE(type);
bool (*page_initializer) (struct page *, enum vm_type, void *);
if (real_type == VM_ANON) {
page_initializer = anon_initializer;
}
else if (real_type == VM_FILE) {
page_initializer = file_backed_initializer;
}
else {
return false; // 지원하지 않는 타입일때
}
page -> va = upage; // 문제 수정중
uninit_new(page, upage, init, type, aux, page_initializer);
/* TODO: Insert the page into the spt. */
// 해당 페이지를 spt에 삽입합니다.
page -> writable = writable; // 이 코드 없으면 pml4_set_page()는 정확한 권한 매핑불가
if (!spt_insert_page (spt, page))
return false;
return true;
}
}
uninit.c
uninit_initialize
첫 번째 오류 발생시에 페이지를 초기화하는 함수입니다. 일단 지금은 고칠게 없습니다.
// 첫 번째 오류 발생 시 페이지 초기화합니다.
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;
// 이 기능을 수정해야 할 수도 있습니다.
return uninit->page_initializer (page, uninit->type, kva) &&
(init ? init (page, aux) : true);
}
vm.c
vm_anon_init
익명 페이지 하위 시스템을 초기화합니다. 해당 함수도 바꿀 내용이 현재로선 없습니다.
// 익명 페이지에 대한 데이터 초기화를 수행합니다.
void
vm_anon_init (void) {
// /* TODO: Set up the swap_disk. */
// swap_disk를 설정하세요.
swap_disk = NULL;
}
anon_initializer
익명 페이지에 대한 핸들러를 설정합니다. 해당 함수도 바꿀 내용이 없습니다.
// 파일 매핑을 초기화합니다.
bool
anon_initializer (struct page *page, enum vm_type type, void *kva) {
/* Set up the handler */
page -> operations = &anon_ops; // 페이지가 anon으로 동작
// page -> anon.aux = aux;
struct anon_page *anon_page = &page->anon;
// anon_page->swap_slot = -1; // 아직 스왑되지 않았음을 나타냄
// return true;
}
process.c
거의 맨 밑에 부분에 load_segment
및 lazy_load_segment
함수들을 구현해야합니다.
load_segment
vm_alloc_page_with_initializer
함수에 lazy_load_segment
정보를 전달하기 위해 aux
를 설정합니다.
특히, file_read를 하지 않기 때문에 수동으로 ofs
를 이동시켜줘야하는 코드도 추가해야합니다. ofs
는 해당 page
가 로딩될 때, 파일의 어디서부터 읽어야 할지를 알려주는 정보입니다. 해당 코드가 없다면 page fault가 나와도 모든 페이지가 파일의 같은 위치를 중복해서 읽게되어 잘못된 데이터를 가져올 수 있습니다.
// 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를 설정합니다.
struct lazy_load_info *aux = malloc(sizeof(struct lazy_load_info));
aux -> file = file;
aux -> ofs = ofs;
aux -> upage = upage;
aux -> read_bytes = page_read_bytes;
aux -> zero_bytes = page_zero_bytes;
aux -> writable = writable;
if (!vm_alloc_page_with_initializer(VM_FILE, upage,
writable, lazy_load_segment, aux))
return false;
/* Advance. */
read_bytes -= page_read_bytes;
zero_bytes -= page_zero_bytes;
upage += PGSIZE;
/* file_read를 하지 않기 때문에 수동으로 ofs을 이동시켜줘야 한다. */
ofs += page_read_bytes;
}
return true;
}
위의 aux 설정시, lazy_load_info 라는 구조체를 사용하기 때문에 해당 함수 상단에 작성해줘야합니다.
struct lazy_load_info { // lazy를 로드하기 위해 인포를 받음
struct file *file;
off_t ofs;
uint8_t *upage;
uint32_t read_bytes;
uint32_t zero_bytes;
bool writable;
};
lazy_load_segment
실행 파일의 페이지 초기화 함수입니다. 페이지 오류 발생시 호출되고, 페이지 구조체와 aux를 인수로 받습니다. aux를 통해 load_segment를 설정합니다. 이 정보를 사용하여 세그먼트를 읽을 파일을 찾은 후 메모리로 읽어야 합니다.
fileseek
함수를 통해 파일 시작부분을 찾습니다. 그러므로 해당 코드는 빠져선 안됩니다. 그외에 file_read
나 memset
과 같은 함수를 사용했습니다.
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;
file_seek(info->file, info -> ofs);
uint8_t *kva = page->frame->kva;
// 파일 읽기 검증 (실제 바이트와 읽어야하는 info바이트 수 비교)
if (file_read(info -> file, kva, info -> read_bytes) != (int) info -> read_bytes)
return false;
memset(kva + info -> read_bytes, 0, info -> zero_bytes);
return true;
}
setup_stack
새로운 메모리 관리 시스템에 맞게 스택을 조정해야합니다. 그러므로 해당 함수를 통해 USER_STACK에 스택의 페이지를 생성합니다. 성공시에는 true를 반환해줍니다.
TODO 에 따라 작성하였습니다. 하다가 막히면 아래와 같이 printf를 직접 찍어보면서 디버깅하는 것도 한 가지 좋은 분석 습관입니다.
진행 순서는 다음과 같습니다.
vm_alloc_page_with_initializer
함수로 스택 페이지에 대한 SPT를 등록합니다. → 이 페이지는 실제로 메모리에 올리는 것이 아니라, lazy allocation (지연 할당)을 위한 보충 페이지 테이블(SPT) 등록입니다.vm_claim_page
함수로 해당 페이지를 실제로 메모리에 올립니다. (프레임 할당)
→vm_claim_page()
는 위에서 등록한stack_bottom
페이지를 메모리에 매핑합니다.spt_find_page
함수로 방금 만든 페이지 객체 찾습니다.
→ 현재 스레드의 SPT에서 stack_bottom 주소에 대응하는 page 구조체를 가져옵니다.- 해당 페이지가 스택이라는 사실을 명시 + rsp 초기화 합니다.
/* Create a PAGE of stack at the USER_STACK. Return true on success. */
// USER_STACK에 스택의 PAGE를 생성합니다. 성공 시 true를 반환합니다.
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 */
// stack_bottom에 스택을 매핑하고 페이지를 즉시 요청합니다.
// 성공하면 rsp를 적절하게 설정합니다.
// 페이지를 스택으로 표시해야 합니다.
// 그 코드를 여기에 쓰세요.
// printf("여기까지 오는거임?\n");
success = vm_alloc_page_with_initializer(VM_ANON | VM_MARKER_0, stack_bottom, true, NULL, NULL);
if (success) {
success = vm_claim_page(stack_bottom); // 물리 프레임 할당
if (success) {
struct page *page = spt_find_page(&thread_current() -> spt, stack_bottom);
if (page != NULL) {
// printf("여기까지 오는거임?\n");
page -> is_stack = true;
if_ -> rsp = USER_STACK;
}
else success = false;
}
}
// printf("셋업 스택 성공했다!\n");
return success;
}
vm_try_handle_fault
spt_find_page
를 통해 보충 페이지 테이블을 참조하여 오류가 발생한 주소에 해당하는 페이지 구조를 확인하도록 구현해야합니다.
spt_find_page 와 VM_TYPE 함수를 통해 페이지가 가상 주소인지 확인하고, 페이지를 넘겨줍니다.
// 성공 시 true를 반환합니다.
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;
//void *va = pg_round_down(addr);
/* TODO: Validate the fault */
/* TODO: Your code goes here */
/* valid address인지 확인 */
page = spt_find_page(spt, addr);
if(page == NULL) {
return false;
}
enum vm_type type = page->operations->type;
if(VM_TYPE(type) != VM_UNINIT) return false;
return vm_do_claim_page (page);
}
여기까지 진행한다면, 프로젝트2에서의 fork 관련 테스트 케이스 빼고 모두 실행 가능합니다.
(Process exit codes are excluded for matching purposes.)
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
FAIL tests/userprog/fork-once
FAIL tests/userprog/fork-multiple
FAIL tests/userprog/fork-recursive
FAIL tests/userprog/fork-read
FAIL tests/userprog/fork-close
FAIL tests/userprog/fork-boundary
FAIL tests/userprog/exec-once
FAIL tests/userprog/exec-arg
FAIL tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
FAIL tests/userprog/exec-read
FAIL tests/userprog/wait-simple
FAIL tests/userprog/wait-twice
FAIL tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
FAIL tests/userprog/multi-recurse
FAIL tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
FAIL tests/userprog/rox-child
FAIL 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
FAIL 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
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
54 of 141 tests failed.
해당 코드를 구현하면서 생긴 트러블 슈팅은 날짜별 TIL에 적어 두었습니다.
앞으로는 나머지 테스트 케이스인 fork를 통과 가능하게 만들어 볼 예정입니다.
'크래프톤 정글(PintOS WEEK 9 ~ 14)' 카테고리의 다른 글
Devops 운영진 티타임 (2) | 2025.06.13 |
---|---|
PintOS Project3: Virtual Memory (Revisit, Stack Growth, MMAP) (1) | 2025.06.13 |
WEEK 13 PintOS TIL(6월12일 목요일) (4) | 2025.06.13 |
Page fault (Pintos, 발표자료) (2) | 2025.06.13 |
13주차 퀴즈 복습 (1) | 2025.06.13 |