vm/vm.c: 가상 메모리 전반을 관리하는 인터페이스frame.c: 프레임 할당 및 교체 로직anon.c: Anonymous page (파일 없이 메모리에만 존재하는 페이지) 처리file.c: File-backed page 처리 (mmap 등)page.c: Supplemental Page Table(SPT) 핵심 로직 (페이지 등록/탐색 등)uninit.c: Lazy loading을 위한 초기화 구조userprog/exception.c: do_page_fault()에서 Page Fault 핸들링process.c: 프로세스 생성 시 SPT 초기화threads/palloc.c: 페이지 단위 물리 메모리 할당synch.c: 동기화 (락 등)thread.c: 현재 스레드 정보 접근프로젝트 목표: 가상 메모리(Virtual Memory)를 구현하여 사용자 프로그램의 주소 공간을 효율적으로 관리
학습 내용 : SPT, 프레임 관리, 페이지 폴트 처리, 스택 확장, mmap, 스왑 등) 숙지
핵심 기능
mmap)테스트 기준: tests/vm/*, tests/userprog/* 일부 테스트 통과가 목표
책임 구조
Supplemental Page Table (SPT)로 관리한다핵심 파일
vm/page.c: 페이지 등록/탐색 (spt_find_page(), spt_insert_page())vm/vm.c: vm_alloc_page(), vm_claim_page() 등 인터페이스userprog/exception.c: do_page_fault()에서 페이지 폴트 처리anon.c)역할: 파일 없이 메모리로만 구성된 페이지 (ex. Stack, Heap)
anon_page_init() 구현anon_initializer() 구현 및 uninit_new()에서 등록anon_swap_in(), anon_swap_out()은 초기에는 간단히 구현vm/lazy-anonvm/swap-anon (스왑 구현 후)page.c)역할: 가상 주소 ↔ 페이지 정보 매핑
page_hash() / page_less() / page_destroy() 함수 등록spt_find_page(): 주소에 해당하는 페이지 탐색spt_insert_page(): 주소에 새 페이지 등록spt_remove_page(), supplemental_page_table_copy() 등구조체
struct pagestruct supplemental_page_tableuninit.c, do_page_fault())역할: 페이지를 처음 접근할 때 실제 메모리를 할당하고, 초기화함
struct uninit_page 구조 이해do_page_fault()에서 SPT에서 찾고, 없다면 vm_stack_growth() 또는 실패 처리vm_claim_page()에서 initializer() 호출 -> anon_initializer() 등do_page_fault(), vm_do_claim_page(), uninit_initialize()vm/lazy-*vm/pt-* 일부역할: 사용자가 스택을 점점 아래로 확장할 때 동적으로 페이지를 추가
vm_stack_growth() 구현do_page_fault()에서 fault 주소가 스택으로 확장 가능한 범위인지 확인 (is_stack_growth())PHYS_BASE 기준으로 아래쪽으로 성장vm/pt-grow-stackvm/pt-big-stk-objfile.c)역할: 파일 내용을 페이지로 매핑 (mmap 등)
file_backed_initializer(), file_backed_swap_in/out() 등 구현vm_alloc_page_with_initializer() 활용do_mmap(), do_munmap() 구현vm/mmap-*vm/page-merge-*, vm/page-shuffle역할: 메모리가 부족할 때 디스크로 페이지를 임시 저장하는 기능
swap.c에 bitmap 기반 슬롯 관리frame.c: 프레임 부족 시 교체 (Clock 등)anon_swap_in(), anon_swap_out() 완성vm/swap-*vm/lazy-anon 등역할: fork() 시 페이지 복사 대신 공유하고, 쓰기 시 복사
page->writable을 false로 설정page fault 시 복사 수행vm/cow/cow-simple| 단계 | 구현 파일 | 주요 테스트 |
|---|---|---|
| Anonymous | anon.c | lazy-anon, swap-anon |
| SPT 연결 | page.c | 모든 테스트 기반 |
| Lazy Loading | uninit.c, do_page_fault | lazy-*, pt-* |
| Stack Growth | vm.c, exception.c | pt-grow-*, pt-big-stk-obj |
| File-backed | file.c | mmap-*, page-merge-* |
| Swap | swap.c, frame.c | swap-* |
| COW | page.c, exception.c | cow-simple |
주요 파일: userprog/exception.c의 page_fault() 함수, vm/vm.c (또는 유사 파일) 내의 페이지 로딩 및 스택 확장 로직.
테스트 그룹: tests/vm/pt-*.c (Page Table tests)
%rsp) 근처의 유효한 영역에서 페이지 폴트가 발생하면, 스택을 자동으로 한 페이지씩 확장해줍니다.pt-grow-stack: 정상적인 스택 확장 (한 페이지).pt-grow-bad: 부적절한 주소에서의 스택 확장 시도 시 프로세스 종료.pt-big-stk-obj: 큰 객체 할당을 위한 다중 페이지 스택 확장.pt-bad-addr, pt-bad-read: 유효하지 않은 주소 접근 시 프로세스 종료.pt-write-code, pt-write-code2: 읽기 전용인 코드 영역에 쓰기 시도 시 프로세스 종료.pt-grow-stk-sc: 시스템 콜 중 발생하는 스택 확장의 정상 처리.주요 파일: vm/vm.c, vm/anon.c, vm/file.c, vm/uninit.c (KAIST Pintos 기준). struct page의 다양한 타입(VM_ANON, VM_FILE, VM_UNINIT) 및 관련 핸들러 함수들.
테스트 그룹: tests/vm/page-*.c, tests/vm/lazy-*.c
VM_ANON): 스택, 힙, 또는 BSS 세그먼트처럼 특정 파일과 연결되지 않은 페이지들을 관리합니다. 처음 접근 시 0으로 초기화된 물리 프레임에 연결됩니다 (지연 로딩).VM_FILE): 실행 파일의 코드/데이터 세그먼트나 메모리 매핑된 파일의 내용을 담는 페이지입니다. 처음 접근 시 파일로부터 해당 내용을 읽어와 물리 프레임에 로드합니다 (지연 로딩).VM_UNINIT): 페이지가 처음 요청되었지만 아직 내용이 로드되지 않았거나 타입이 결정되지 않은 상태를 나타냅니다. 페이지 폴트 시 실제 타입(ANON 또는 FILE)으로 전환되며 초기화됩니다.page-linear, page-parallel, page-shuffle: 익명 페이지의 기본적인 할당, 접근, 해제 및 여러 프로세스/스레드에서의 동시 사용 시나리오를 검증합니다.page-merge-*: (Pintos 기본 요구사항은 아닐 수 있으나) 인접한 페이지들의 관리 효율성을 간접적으로 테스트할 수 있습니다.lazy-anon: 익명 페이지가 실제로 접근될 때까지 물리 메모리 할당 및 초기화가 지연되는지 확인합니다.lazy-file: 파일 기반 페이지가 실제로 접근될 때까지 파일로부터 데이터 로딩이 지연되는지 확인합니다.주요 파일: userprog/syscall.c (mmap, munmap 시스템 콜 처리), vm/file.c (파일 기반 페이지 처리 로직 연관).
테스트 그룹: tests/vm/mmap-*.c
mmap 시스템 콜: 지정된 파일을 사용자 프로세스의 가상 주소 공간의 특정 영역에 매핑합니다.VM_FILE)들을 설정합니다. 이때도 지연 로딩을 사용하여, 실제 메모리 접근이 발생할 때까지 파일 내용을 물리 메모리로 읽어오지 않습니다.munmap 시스템 콜: 기존의 메모리 매핑을 해제합니다.munmap 처리되어야 합니다.mmap-read, mmap-write: 매핑된 메모리를 통한 파일 읽기 및 쓰기, 쓰기 후 파일 반영(write-back) 확인.mmap-close: 매핑 후 원본 파일 디스크립터를 닫아도 매핑이 유지되는지 확인.mmap-unmap: 정상적인 매핑 해제 및 해제 후 접근 시도 시 오류 처리.mmap-exit: 프로세스 종료 시 자동 unmap 및 dirty 페이지의 write-back.mmap-ro: 읽기 전용 매핑 후 쓰기 시도 시 오류 처리.mmap-overlap, mmap-twice, mmap-bad-fd, mmap-misalign, mmap-null, mmap-over-code/data/stk, mmap-zero, mmap-zero-len, mmap-off, mmap-kernel 등등.주요 파일: vm/vm.c (또는 프레임 관리 및 페이지 교체 로직이 있는 파일), vm/anon.c (익명 페이지 스왑 처리), devices/disk.c (스왑 디스크 사용).
테스트 그룹: tests/vm/swap-*.c
swap-anon: 익명 페이지의 정상적인 스왑 아웃 및 스왑 인 동작 확인.swap-file: 파일 기반 페이지의 스왑 아웃 및 스왑 인 동작 확인.swap-iter: 반복적인 스왑 발생 시 데이터 무결성 및 시스템 안정성 확인.swap-fork: fork와 스와핑이 동시에 발생하는 복잡한 상황에서의 안정성 테스트.주요 파일: userprog/process.c (fork 구현 시), vm/vm.c (또는 페이지 관리 로직).
테스트 그룹: tests/vm/cow-simple.c (KAIST Pintos의 경우 tests/vm/cow/ 하위에 있을 수 있음)
fork 시스템 콜을 통해 자식 프로세스를 생성할 때, 부모의 메모리 공간을 즉시 물리적으로 복사하는 대신, 부모와 자식이 동일한 물리 프레임을 공유하도록 합니다. 이때 해당 페이지들은 읽기 전용(read-only)으로 표시됩니다.fork의 성능을 향상시키고 메모리 사용량을 줄일 수 있습니다.cow-simple: fork 후 부모/자식 중 한쪽에서 공유 페이지에 쓰기를 시도할 때 COW가 정상적으로 동작하여 해당 페이지만 복사되고, 다른 쪽의 페이지는 영향을 받지 않는지 확인합니다.직접 흐름을 파악하는 것을 추천합니다. 참고용 입니다.
main() (threads/init.c)palloc_init(): 물리 메모리 페이지 할당자 초기화 (이미 프로젝트 1, 2에서 사용).vm_init() (또는 vm_anon_init, vm_file_init 등): 가상 메모리 서브시스템의 전역적인 자료구조 초기화.vm/vm.c 또는 유사 파일)vm/vm.c 또는 vm/anon.c, devices/disk.c 연관)process_create_initd() / process_exec() (userprog/process.c)thread_create() (threads/thread.c)init_thread() 내부 또는 직후:struct thread에 SPT 포인터 필드를 추가하고, 이 함수 내에서 supplemental_page_table_init(&curr->spt) (가칭) 같은 함수를 호출합니다. (vm/vm.c 또는 유사 파일)load() (userprog/process.c): 실행 파일을 메모리에 적재할 때load_segment() 함수가 호출됩니다.load_segment() 내부 (수정 필요):palloc_get_page()로 물리 프레임을 직접 할당하고 install_page()로 매핑했습니다.VM_FILE (실행 파일의 내용을 담으므로) 또는 VM_UNINIT (지연 로딩을 위해)으로 설정하고, 파일 정보(실행 파일, 오프셋, 읽을 크기 등)를 함께 저장합니다.vm_alloc_page_with_initializer(VM_FILE, upage, writable, lazy_load_segment, file_info) (가칭, vm/vm.c)pg_fault_handler() (IDT에 등록된 핸들러, threads/intr-stubs.S에서 호출)page_fault() (userprog/exception.c) - 여기가 핵심 수정 지점입니다!struct intr_frame *f에서 폴트가 발생한 가상 주소(fault_addr), 오류 코드(error_code), 스택 포인터(f->rsp 또는 64비트에서는 f->R.rsp), 유저/커널 모드 등을 가져옵니다.fault_addr가 유효한 사용자 영역 주소인가? (is_user_vaddr(), pml4_get_page()로 커널 매핑 확인 등)struct page *page = spt_find_page(&thread_current()->spt, fault_addr) (가칭, vm/vm.c)fault_addr가 현재 스택 포인터 근처이고, 유효한 스택 확장 범위 내인지 검사합니다. (예: fault_addr >= f->R.rsp - PUSH_SAVE_AREA 이고 fault_addr < USER_STACK 이고 USER_STACK - fault_addr <= MAX_STACK_SIZE)vm_stack_growth(fault_addr) (가칭, vm/vm.c) 호출VM_ANON)를 fault_addr에 해당하는 페이지 경계 주소로 SPT에 할당 (vm_alloc_page(VM_ANON, pg_round_down(fault_addr), true)).vm_claim_page()를 통해 물리 프레임을 할당받고, SPT와 프레임을 연결하고, CPU 페이지 테이블(PML4)에 매핑합니다. 성공하면 page_fault() 핸들러 종료.exit(-1)).page 구조체에 있는 operations (함수 포인터 테이블)을 사용하여 해당 페이지 타입에 맞는 핸들러를 호출합니다.vm_handle_fault(struct page *page, void *fault_addr, bool write, struct intr_frame *f) (가칭, vm/vm.c)page->operations->fault() (예: uninit_fault, anon_fault, file_fault)를 호출할 수 있습니다.uninit_fault 또는 file_fault의 일부):vm_get_frame() (가칭, vm/vm.c) 호출.vm_select_victim() 가칭).victim_frame)의 페이지(victim_page)가 dirty하면, victim_page->operations->swap_out(victim_page) 호출하여 디스크(스왑 또는 원본 파일)로 내보냄.victim_frame을 정리 (PML4에서 매핑 해제, victim_page->frame = NULL).frame), page->frame = frame; frame->page = page; 연결.VM_UNINIT 페이지: page->uninit.initializer(page, page->uninit.aux) 호출 (예: lazy_load_segment 실행).VM_ANON (스왑된 경우): anon_swap_in(page, frame->kva) 호출.VM_FILE (스왑 또는 처음 로드): file_backed_swap_in(page, frame->kva) 또는 파일에서 직접 읽기.pml4_set_page(cur->pml4, page->va, frame->kva, page->writable) 호출.page_fault() 핸들러 종료.error_code & PTE_W) 등을 확인하여 처리. 실패 시 프로세스 종료.mmap, munmap)syscall_handler() (userprog/syscall.c)SYS_MMAP 요청 시:addr부터 addr + length)에 대해 루프를 돌면서 각 페이지(upage) 처리:upage가 있는지 확인 (겹치면 에러).vm_alloc_page_with_initializer(VM_FILE, upage, writable, lazy_load_file_segment, file_info) (가칭) 호출하여 SPT에 파일 기반 미초기화 페이지로 등록. file_info에는 매핑할 파일, 오프셋, 읽을 바이트 수 등을 저장.addr) 반환.SYS_MUNMAP 요청 시:mapping).mapping에 해당하는 모든 페이지들을 SPT에서 찾아 루프:struct page *page = spt_find_page(&thread_current()->spt, target_va);destroy 함수 호출: page->operations->destroy(page) (예: file_backed_destroy).file_backed_destroy 내부:file_write_at())vm_free_frame(page->frame) 호출.anon_free_slot() (만약 파일 기반 페이지도 스왑을 쓴다면) 호출.process_exit)process_exit() (userprog/process.c)supplemental_page_table_kill(&thread_current()->spt) (가칭, vm/vm.c) 호출.struct page에 대해 page->operations->destroy(page) 호출하여 자원 해제 (mmap 해제, 스왑 슬롯 해제, 프레임 반납 등).pml4_destroy(curr->pml4): CPU의 페이지 테이블(PML4) 해제 (이 과정에서 프레임들도 해제될 수 있음).vm_get_frame() (가칭, vm/vm.c): 새로운 물리 프레임을 요청할 때 호출됨 (주로 페이지 폴트 핸들러에서).palloc_get_page(PAL_USER)로 사용자 풀에서 프레임 할당 시도.vm_evict_frame() 또는 vm_select_victim() (가칭) 호출하여 페이지 교체 알고리즘 실행.victim_frame에 연결된 victim_page를 처리:victim_page의 매핑 제거 (pml4_clear_page()).victim_page->operations->swap_out(victim_page) 호출하여 디스크로 내보내기. (dirty 비트 확인)victim_page->frame = NULL; (연결 해제)victim_frame은 비었으므로, 이 프레임을 현재 요청한 페이지를 위해 사용. 프레임 테이블 정보 업데이트 후 반환.vm_free_frame(struct frame *frame) (가칭, vm/vm.c): 프레임이 더 이상 필요 없을 때 (예: 페이지가 destroy될 때).palloc_free_page(frame->kva).프로젝트 3의 핵심 기능들에 대한 대략적인 코드 흐름. 실제로 구현할 때는 include/vm/vm.h에 정의된 struct page, struct frame, struct page_operations 등의 자료구조를 중심으로 각 함수들을 채워나가기
특히 vm_alloc_page_with_initializer, vm_claim_page, spt_find_page, vm_handle_fault, vm_get_frame, 페이지 타입별 initializer, fault, swap_in, swap_out, destroy 함수들이 주로 구현하게 될 부분