Project3 - Virtual Memory 이번 주 목표
1) Memory Management
2) Anonymous Page

가상메모리 파트부터 Supplemental Page Table이 추가되면서, process_exec() 함수가 실행되기 전, spt 초기화를 먼저 진행. (supplement_page_table_init)
- SPT를 디자인 하는 과정에서 비트맵, 리스트, 해시테이블 등 여러 선택지가 있음. 각 선택지에 맞춰 supplement_page_table_init() 함수 내에서 초기화를 진행함.
- 해시테이블 선택시, hash_init() 함수로 초기화 진행
process_exec() 함수 실행 중, process_cleanup() 함수에서 현재 프로세스의 자원을 해제할 때, 해당 프로세스의 SPT 또한 더 이상 유지할 필요가 없으므로 함께 제거하는 과정이 추가됨 (supplemental_page_table_kill)
load() 함수에서 페이지 디렉토리 생성 및 활성화, 로드할 세그먼트의 유효성 검사, 스택 설정 등 프로세스의 실행환경을 구축
load_segment() 함수가 여기서 중요한데, 여기를 시작으로 SPT에서 페이지를 검색하고 초기화하거나, 페이지가 존재하지 않는 경우 새로운 페이지를 생성하여 SPT에 삽입하는 과정이 진행됨(빨간색 박스 부분)
| 기존 | 수정 후 | |
|---|---|---|
| 방식 | eager loading | lazy loading |
| 역할 | 파일의 세그먼트를 페이지 단위로 메모리에 로드 | 가상 주소 공간에 페이지 단위로 메모리를 로드 & 매핑 |
| Accessdata | 모든 데이터&리소스 | 필수 데이터&리소스 |
| 페이지 매핑 | 페이지를 프로세스의 주소 공간에 직접 매핑 | 가상 주소를 물리 주소를 매핑하고, 페이지를 프로세스 주소 공간에 추가 |
| 주소 공간 확장 | 주소 공간의 크기가 파일 크기보다 작을 경우, Fail 처리 | 주소 공간의 크기가 파일 크기보다 작을 경우, 추가 페이지 할당하여 주소 공간 확장 |
VM 구현 전, load_segment() 함수는 모든 데이터와 리소스를 메모리에 로드했음. (페이지를 물리메모리에 직접 매핑)
- 물리프레임으로부터 할당을 받고, 파일을 해당 프레임에 로드한 후에 페이지 테이블에서 가상주소와 물리주소를 매핑하는 방식이었음
Project 3에서는 lazy_loading 방식으로 바꿔야 하기 때문에, 가상 페이지를 먼저 할당한 뒤(vm_alloc_page_with_initializer), 가상주소와 물리주소를 매핑하여 페이지르르 프로세스의 주소공간에 추가하는 방식으로 변경해야 함
핀토스에서 기존 load_segment() 함수는 처음 파일을 읽어 올 때 파일 전체를 읽어오는데, 실제 파일 비트 개수와 load_segment 함수로 읽어온 파일의 비트 개수가 다름
그 이유는, ELF 포맷에서는 ELF 헤더처럼 ELF 포맷 상에는 존재하지만 메모리에는 로드되지 않는 부분이 있기 때문임. 단순히 응용프로그램 크기와 load_segment에서 읽은 용량이 같지 않을 수 있음.
3) stack_growth
4) memory mapped files
5) swap out
Paging_init : main 함수에서, 메모리 시스템을 초기화 할 때 사용되는 함수.
mmu.c 파일 → pml4 관련 모든 함수 : 마찬가지로 pml4 관련 모든 함수들에서 페이지 테이블 엔트리의 유효성을 표현할 때 valid bit를 사용.
[ PTE_A 사용 함수 : vm_get_frame → vm_evict_frame → vm_get_victim ]
[ PTE_D 사용 함수 : do_munmap & file_backed_swap_out ]
do_munmap : 주어진 가상주소 범위의 메모리 매핑을 해제하는 함수.
file_backed_swap_out : File_backed page를 스왑 아웃하는 함수.
[ 참고 ]
Dirty Bit : 페이지 변경 여부 저장하는 비트. 변경될 때마다 해당 비트는 1이 되고, 디스크에 변경 내용을 기록하면 0으로 초기화 됨.
do_munmap 과 file_backed_swap_out에서 더티 비트는 비슷한 플로우로 사용됨.
어떤 페이지가 Swap Out 될 때, 해당 페이지가 수정되지 않았다면 스왑 아웃할 필요가 없음. 동일한 내용이 Disk 어딘가에 있기 때문임.
따라서, 페이지의 수정 여부를 알 수 있다면, 페이지를 교체할 때 스왑 아웃 시간을 절약할 수 있음. (Swap In 만 해주면 되기 때문)
이 때, 페이지의 수정여부를 알려주는 값이 바로 Dirty Bit임!
—> 페이지 폴트가 발생하고 모드 전환되면 유저 프로그램의 스택 포인터 값이 인터럽트 프레임에 저장되어 페이지 폴트 핸들러나 시스템콜 핸들러에 인자로 전달되기 때문!
/* syscall.c */
void syscall_handler (struct intr_frame *f) {
#ifdef VM
thread_current()->rsp_stack = f->rsp;
#endif
...
}