Swap In/Out을 구현했지만 페이지 교체 알고리즘을 구현해야 테스트를 통과할. 수 있다. 그리고 추가적인 조정을 통해 cow-simple 테스트를 제외한 모든 테스트를 통과할 수 있도록 구현할 예정이다.
페이지 교체 알고리즘
페이지 교체 알고리즘은 운영체제가 메모리가 부족할 때 어떤 페이지를 내보낼지 결정하는 방식이다. 즉 RAM에 올려둘 수 없는 페이지를 디스크(Swap)를 내보낼 때 어떤 것을 회생할지 선택하는 규칙이다.
페이지 교체 알고리즘이 필요한 이유
- 운영체제는 가상 메모리를 통해 실제 메모리 보다 많은 메모리를 제공하는 것처럼 동작한다.
- 하지만 실제 물리 메모리(RAM)는 제한되어 있어, 더 이상 올릴 수 없는 경우 기존 페이지를 내보내야(Swap Out) 한다.
- 이때 어떤 페이지를 내보낼지 결정하는 것이 페이지 교체 알고리즘이다.
알고리즘 | 설명 | 장단점 |
FIFO (First-In, First-Out) | 가장 오래된 페이지를 제거 | 단순하지만 성능 낮음 |
LRU (Least Recently Used) | 가장 오랫동안 사용되지 않은 페이지 제거 | 실제 구현 어려움 (정확한 시간 추적 필요) |
Clock (Second Chance) | FIFO 개선형, 최근 사용 여부 비트(Accessed bit)로 판단 | 성능 좋고 구현 쉬움 |
Random | 무작위 선택 | 예측 불가, 단순 실험용 |
NFU / Aging | 사용 빈도에 따라 점수 매김 | 구현 가능, LRU 근사 |
나는 FIFO를 이용해 구현을 할 것이다. 이유는 단순하다는 것이다.
페이지 교체 구현
frame 테이블 선언
- include/vm/vm.h
#endif
struct page_operations;
struct thread;
struct list frame_table;
- 프레임을 관리하기 위한 리스트를 추가한다.
/* The representation of "frame" */
struct frame {
void *kva;
struct page *page;
struct list_elem elem;
};
- 프레임을 리스트에 넣기 위해 list_elem을 프레임 구조체에 추가한다.
vm_init 함수 수정
- vm/vm.c
/* 각 서브시스템의 초기화 코드를 호출하여 가상 메모리 서브시스템을 초기화합니다. */
void
vm_init (void)
{
vm_anon_init();
vm_file_init();
#ifdef EFILESYS /* For project 4 */
pagecache_init();
#endif
register_inspect_intr();
/* 이 위쪽은 수정하지 마세요 !! */
/* TODO: 이 아래쪽부터 코드를 추가하세요 */
list_init(&frame_table);
}
- 헤더 파일에 정의했던 리스트를 vm_init에서 초기화한다.
vm_get_frame 수정
- vm/vm.c
* palloc()을 사용하여 프레임을 할당합니다.
* 사용 가능한 페이지가 없으면 페이지를 교체(evict)하여 반환합니다.
* 이 함수는 항상 유효한 주소를 반환합니다. 즉, 사용자 풀 메모리가 가득 차면,
* 이 함수는 프레임을 교체하여 사용 가능한 메모리 공간을 확보합니다.*/
static struct frame *
vm_get_frame(void)
{
/* TODO: Fill this function. */
struct frame *frame = NULL;
// 1. 유저 풀에서 새로운 페이지 할당
void *kva = palloc_get_page(PAL_USER | PAL_ZERO);
// 2. 할당 실패 시 PANIC
if (kva == NULL) {
struct frame *evicted = vm_evict_frame();
if (evicted == NULL)
PANIC("Eviction failed: No frame available");
evicted->page = NULL;
list_push_back(&frame_table, &evicted->elem);
return evicted;
}
// 3. 프레임 구조체 할당 및 초기화
frame = (struct frame *)malloc(sizeof(struct frame));
if (frame == NULL)
PANIC("Failed to allocate frame metadata");
frame->kva = kva;
frame->page = NULL;
list_push_back(&frame_table, &frame->elem);
return frame;
}
이전에 구현했던 vm_get_frame 함수에 물리 메모리 할당을 실패했을 때 페이지 교체를 발생하도록 수정을 해야 한다.
if (kva == NULL) {
struct frame *evicted = vm_evict_frame();
if (evicted == NULL)
PANIC("Eviction failed: No frame available");
evicted->page = NULL;
list_push_back(&frame_table, &evicted->elem);
return evicted;
}
- 사용 가능한 페이지가 없을 경우, 페이지 교체 알고리즘을 통해 프레임을 회수한다.
- vm_evict_frame() 함수는 희생 프레임을 선택하고, 스왑 아웃까지 수행하여 메모리를 확보한다.
- evicted->page = NULL: 새롭게 재사용할 수 있도록 페이지 연결을 제거한다.
- 반환 전에 프레임 테이블에 재등록한다 (frame_table은 모든 사용 중인 프레임을 추적하는 리스트).
- 프레임 교체 실패 시, PANIC을 호출하여 시스템 중단.
frame->kva = kva;
frame->page = NULL;
list_push_back(&frame_table, &frame->elem);
- frame_table에 이 프레임을 등록하여 전역 프레임 관리에 포함시킨다
vm_evict_frame 구현
- vm/vm.c
/* 한 페이지를 교체(evict)하고 해당 프레임을 반환합니다.
* 에러가 발생하면 NULL을 반환합니다.*/
static struct frame *
vm_evict_frame (void)
{
struct frame *victim = vm_get_victim ();
/* TODO: swap out the victim and return the evicted frame. */
if (victim == NULL)
return NULL;
struct page *page = victim->page;
if (page == NULL)
PANIC("Victim has no page");
if (!swap_out(page))
return NULL;
victim->page = NULL;
page->frame = NULL;
pml4_clear_page(thread_current()->pml4, page->va);
return victim;
}
vm_evict_frame 함수는 페이지 교체 알고리즘을 통해 프레임 하나를 회수하고 해당 프레임을 재사용 가능한 상태로 반환하는 역할을 한다. 메모리가 부족할 때 vm_get_frame 이 호출하여 새로운 프레임을 확보할 수 있도록 돕는다.
struct frame *victim = vm_get_victim ();
if (victim == NULL)
return NULL;
- vm_get_victim() 함수는 페이지 교체 알고리즘(예: Clock 알고리즘)에 따라 회수할 프레임을 선택한다.
- 회수할 수 있는 프레임이 없으면 NULL을 반환한다.
struct page *page = victim->page;
if (page == NULL)
PANIC("Victim has no page");
- 회수 대상 프레임이 실제로 연결된 페이지가 있는지 확인한다.
- 프레임이 비어 있거나 잘못된 상태면 치명적인 오류로 간주하여 커널을 중단시킨다.
if (!swap_out(page))
return NULL;
- swap_out(page) 함수는 해당 페이지를 디스크로 내보낸다 (스왑 아웃).
- 내부적으로 page->operations->swap_out(page)을 호출하여 anon 또는 file 페이지에 맞는 스왑 방식으로 처리된다.
- 스왑 아웃이 실패하면 NULL을 반환한다.
victim->page = NULL;
page->frame = NULL;
- 해당 페이지와 프레임 간의 연결을 끊는다.
- 이 프레임은 이제 빈 프레임이 되며, 다른 페이지가 사용할 수 있는 상태가 된다.
pml4_clear_page(thread_current()->pml4, page->va);
- 현재 프로세스의 페이지 테이블에서 해당 가상 주소에 대한 매핑을 제거한다.
- 해당 주소로 접근하면 페이지 폴트가 발생하고, 그때 다시 스왑 인을 통해 복원된다.
return victim;
- 회수 및 정리된 프레임을 반환하여 vm_get_frame() 등에서 재사용하게 한다.
vm_get_victim 구현
- vm/vm.c
/* 교체될 struct frame을 가져옵니다. */
static struct frame *
vm_get_victim (void)
{
if (list_empty(&frame_table))
return NULL;
struct list_elem *e = list_pop_front(&frame_table);
return list_entry(e, struct frame, elem);
}
vm_get_victim 함수는 페이지 교체 대상 프레임을 선택하여 반환하는 함수다. 현재 구현은 매우 단순한 형태고 페이지 교체 알고리즘 중 FIFO 방식으로 동작한다.
if (list_empty(&frame_table))
return NULL;
- frame_table은 현재 사용 중인 모든 프레임을 연결 리스트 형태로 관리하는 전역 리스트다.
- 리스트가 비어 있다면, 교체할 수 있는 프레임이 없다는 뜻이므로 NULL을 반환한다.
struct list_elem *e = list_pop_front(&frame_table);
- list_pop_front()는 리스트의 가장 앞에 있는 요소를 꺼낸다(pop) — 즉, 가장 먼저 들어온 프레임이다.
- 이 방식은 FIFO 알고리즘이며, 가장 오래된 프레임을 희생 대상으로 간주한다.
return list_entry(e, struct frame, elem);
- list_entry()는 리스트 노드(struct list_elem *)를 실제 구조체(struct frame *) 포인터로 변환한다.
- elem은 struct frame 내부에 정의된 리스트 요소 멤버다.
여기까지 구현하면 아마 테스트를 잘 통과하지 않을 것이다. 특히 merge라고 되어있는 테스트들을 통과하지 못하고 있을 수 있다. 이 테스트들은 lock을 잘 설정해야 통과할 수 있다고 한다. 그래서 lock을 설정하거나 몇 가지 수정을 하겠다.
Lock 추가 및 세부 조정
supplemental_page_table_copy 수정
- 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;
if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable, init, aux))
return false;
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;
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);
pml4_set_page(thread_current()->pml4, file_page->va, src_page->frame->kva, src_page->writable);
continue;
}
/* 2) type이 uninit이 아니면 */
if (!vm_alloc_page(type, upage, writable))
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;
}
수정사안
if (!vm_alloc_page_with_initializer(VM_ANON, upage, writable, init, aux))
return false;
- 에러를 처리하기 위한 방어 코드를 추가한다.
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;
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);
pml4_set_page(thread_current()->pml4, file_page->va, src_page->frame->kva, src_page->writable);
continue;
}
이전 코드에 있던
file_page->frame = src_page->frame;
이 코드를 제거했다.
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하여 별도 참조를 유지 (중복 닫힘 방지)
lock_acquire(&filesys_lock);
struct file *f = file_reopen(file);
lock_release(&filesys_lock);
if (file == NULL) {
return NULL;
}
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;
}
lock_acquire(&filesys_lock);
struct file *f = file_reopen(file);
lock_release(&filesys_lock);
if (file == NULL) {
return NULL;
}
- file_reopen을 하는 기능 위아래로 락을 추가하고 에러 방어 코드를 추가했다.
do_munmap 구현
- vm/file.c
/* Do the munmap */
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)
spt_remove_page(spt, p);
// destroy(p);
addr += PGSIZE;
p = spt_find_page(spt, addr);
}
}
- 기존에 destroy를 spt_remove_page로 변경했다.
- spt_remove_page를 호출하면 결국 file_backed_destory도 호출된다.
load_segment 수정
- userprog/process.c
/* Loads an ELF executable from FILE_NAME into the current thread.
* Stores the executable's entry point into *RIP
* and its initial stack pointer into *RSP.
* Returns true if successful, false otherwise. */
static bool
load (const char *file_name, struct intr_frame *if_) {
struct thread *t = thread_current ();
struct ELF ehdr;
struct file *file = NULL;
off_t file_ofs;
bool success = false;
int i;
/* Allocate and activate page directory. */
t->pml4 = pml4_create ();
if (t->pml4 == NULL)
goto done;
process_activate (thread_current ());
lock_acquire(&filesys_lock);
/* Open executable file. */
file = filesys_open (file_name);
if (file == NULL) {
printf ("load: %s: open failed\n", file_name);
goto done;
}
/* Read and verify executable header. */
if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
|| memcmp (ehdr.e_ident, "\177ELF\2\1\1", 7)
|| ehdr.e_type != 2
|| ehdr.e_machine != 0x3E // amd64
|| ehdr.e_version != 1
|| ehdr.e_phentsize != sizeof (struct Phdr)
|| ehdr.e_phnum > 1024) {
printf ("load: %s: error loading executable\n", file_name);
goto done;
}
/* Read program headers. */
file_ofs = ehdr.e_phoff;
for (i = 0; i < ehdr.e_phnum; i++) {
struct Phdr phdr;
#ifdef WSL
// WSL 전용 코드
off_t phdr_ofs = ehdr.e_phoff + i * sizeof(struct Phdr);
file_seek(file, phdr_ofs);
if (file_read(file, &phdr, sizeof phdr) != sizeof phdr)
goto done;
#else
// docker(기본) 전용 코드
if (file_ofs < 0 || file_ofs > file_length(file))
goto done;
file_seek(file, file_ofs);
#endif
if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
goto done;
file_ofs += sizeof phdr;
switch (phdr.p_type) {
case PT_NULL:
case PT_NOTE:
case PT_PHDR:
case PT_STACK:
default:
/* Ignore this segment. */
break;
case PT_DYNAMIC:
case PT_INTERP:
case PT_SHLIB:
goto done;
case PT_LOAD:
if (validate_segment (&phdr, file)) {
bool writable = (phdr.p_flags & PF_W) != 0;
uint64_t file_page = phdr.p_offset & ~PGMASK;
uint64_t mem_page = phdr.p_vaddr & ~PGMASK;
uint64_t page_offset = phdr.p_vaddr & PGMASK;
uint32_t read_bytes, zero_bytes;
if (phdr.p_filesz > 0) {
/* Normal segment.
* Read initial part from disk and zero the rest. */
read_bytes = page_offset + phdr.p_filesz;
zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
- read_bytes);
} else {
/* Entirely zero.
* Don't read anything from disk. */
read_bytes = 0;
zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
}
if (!load_segment (file, file_page, (void *) mem_page,
read_bytes, zero_bytes, writable))
goto done;
}
else
goto done;
break;
}
}
t->running = file;
file_deny_write(file); /** Project 2: Denying Writes to Executables */
/* Set up stack. */
if (!setup_stack (if_))
goto done;
/* Start address. */
if_->rip = ehdr.e_entry;
/* TODO: Your code goes here.
* TODO: Implement argument passing (see project2/argument_passing.html). */
success = true;
done:
lock_release(&filesys_lock);
return success;
}
- file을 오픈하기 전에 락을 추가하고 종료 직전에 락을 해제하도록 수정했다.
mmap 수정
- userprog/syscall.c
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset){
// TODO: 1. 유효성 검사
// - addr이 NULL이 아니고 page-aligned인지 확인
if (!addr || addr != pg_round_down(addr))
return NULL;
if (offset != pg_round_down(offset))
return NULL;
if (!is_user_vaddr(addr) || !is_user_vaddr(addr + length))
return NULL;
if (spt_find_page(&thread_current()->spt, addr))
return NULL;
struct file *f = process_get_file(fd);
if (f == NULL)
return NULL;
if (file_length(f) == 0 || (int)length <= 0)
return NULL;
return do_mmap(addr, length, writable, f, offset);
}
- mmap 검증 조건들을 수정했다.
remove 수정
- userprog/syscall.c
bool remove (const char *file) {
check_address(file);
lock_acquire(&filesys_lock);
bool is_success = filesys_remove(file);
lock_release(&filesys_lock);
return is_success;
}
- 락을 추가했다.
write 수정
- userprog/syscall.c
int write(int fd, const void *buffer, unsigned size) {
check_address(buffer);
lock_acquire(&filesys_lock);
off_t bytes = -1;
if (fd <= 0){ // stdin에 쓰려고 할 경우 & fd 음수일 경우
lock_release(&filesys_lock);
return -1;
}
if (fd < 3) { // 1(stdout) * 2(stderr) -> console로 출력
putbuf(buffer, size);
lock_release(&filesys_lock);
return size;
}
struct file *file = process_get_file(fd);
if (file == NULL){
lock_release(&filesys_lock);
return -1;
}
bytes = file_write(file, buffer, size);
lock_release(&filesys_lock);
return bytes;
}
- 락을 추가했다.
read 수정
- userprog/syscall.c
int read(int fd, void *buffer, unsigned size) {
check_address(buffer);
lock_acquire(&filesys_lock);
if (fd == 0) { // 0(stdin) -> keyboard로 직접 입력
int i = 0; // 쓰레기 값 return 방지
char c;
unsigned char *buf = buffer;
for (; i < size; i++) {
c = input_getc();
*buf++ = c;
if (c == '\0')
break;
}
lock_release(&filesys_lock);
return i;
}
// 그 외의 경우
if (fd < 3) // stdout, stderr를 읽으려고 할 경우 & fd가 음수일 경우
{
lock_release(&filesys_lock);
return -1;
}
struct file *file = process_get_file(fd);
off_t bytes = -1;
if (file == NULL) // 파일이 비어있을 경우
{
lock_release(&filesys_lock);
return -1;
}
#ifdef VM
struct page *page = spt_find_page(&thread_current()->spt, buffer);
if (page && !page->writable){
lock_release(&filesys_lock);
exit(-1);
}
#endif
bytes = file_read(file, buffer, size);
lock_release(&filesys_lock);
return bytes;
}
- 락을 추가했다.
- 가상 메모리 시스템(VM)이 활성화된 상황에서 read()의 대상 주소(buffer)가 유효하고, 쓰기 가능한 주소인지 확인하기 위해서 추가했다.
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
pass tests/vm/page-merge-par
pass tests/vm/page-merge-stk
pass 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
pass tests/vm/swap-file
pass tests/vm/swap-anon
pass 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
1 of 141 tests failed.
- cow-simple을 제외하고 모두 통과하면 구현이 완료된 것이다.
'크래프톤 정글' 카테고리의 다른 글
[pintos] Week4~5: Virtual Memory - Part.6 Swap In/Out (0) | 2025.06.07 |
---|---|
[pintos] Week4~5: Virtual Memory - Part.5 Memory Mapped Files (1) | 2025.06.07 |
[pintos] Week4~5: Virtual Memory - Part.4 Stack Growth (0) | 2025.06.06 |
[pintos] Week4~5: Virtual Memory - Part.3 Anonymous Page (0) | 2025.06.06 |
[pintos] Week4~5: Virtual Memory - Part.2 Memory Management (1) | 2025.06.06 |