실제 우리 프로젝트에서 사용한 코드는 아니다. 전체적 흐름만 파악하기 위한 코드
(참고) 현재 vm_entry 버리고 page 구조체에 spt로 관리
전체 흐름 요약
1. 사용자 가상 주소 (UVA) 0x80400000에 배열이 선언됨.
2. vm_entry가 생성되어 가상 메모리 페이지 정보를 관리.
3. 배열에 처음 접근할 때 페이지 폴트 발생.
4. vm_claim_page가 호출되어 해당 가상 주소를 물리 메모리로 매핑.
5. 프레임을 할당받아 페이지 테이블을 통해 UVA와 KVA 및 물리 주소를 매핑.
6. 배열이 물리 메모리에 할당되고, CPU는 페이지 테이블을 통해 물리 메모리에 접근.
1. 배열 선언 (arr[1024])과 가상 주소 할당
배열을 선언하면, 컴파일러는 배열을 위한 메모리 공간을 가상 주소 공간에서 할당합니다. 이 공간은 UVA 0x80400000으로 매핑될 수 있습니다. 하지만, 이 시점에서 아직 물리 메모리에는 실제로 할당된 페이지가 없습니다.
int arr[1024]; // arr는 UVA 0x80400000에 위치한다고 가정
2. vm_entry와 page 생성
프로세스가 배열을 사용하려고 시도할 때, 가상 주소에 해당하는 페이지를 메모리에 로드하기 위해 vm_entry가 만들어집니다.
vm_entry는 이 페이지에 대한 메타데이터를 저장하는 구조체입니다. 페이지가 아직 메모리에 로드되지 않았을 수 있으므로 is_loaded 필드는 false로 설정됩니다.
👇vm_entry 생성 코드 예시
struct vm_entry *vme = palloc_get_page(0); // vm_entry 메모리 할당
vme->type = VM_ANON; // 익명 페이지
vme->vaddr = 0x80400000; // 가상 주소
vme->writable = true; // 쓰기 가능
vme->is_loaded = false; // 아직 물리 메모리에 로드되지 않음
vme->file = NULL; // 파일 매핑 없음
vme->offset = 0; // 파일 오프셋 없음
vme->read_bytes = PGSIZE; // 읽을 바이트 수
vme->zero_bytes = 0; // 0으로 초기화할 바이트 수
vme->swap_slot = -1; // 스왑 슬롯 (초기 상태)
insert_vme(&thread_current()->spt.vm, vme); // vm 해시 테이블에 삽입
이 코드에서 vm_entry는 가상 주소 UVA 0x80400000이 속한 페이지에 대한 정보를 관리합니다.
3. 페이지 폴트 발생
배열 arr에 접근하려고 할 때, 해당 가상 주소(예: 0x80400000)에 대응하는 페이지가 아직 물리 메모리에 로드되지 않은 상태이므로, 페이지 폴트가 발생합니다.
👇페이지 폴트 처리
page_fault_handler(...){
// 폴트 난 주소를 확인 (여기서는 0x80400000)
void *fault_addr = get_fault_addr();
// 해당 주소에 대한 vm_entry 검색
struct vm_entry *vme = find_vme(fault_addr);
if (vme == NULL) {
// 주소에 대한 vm_entry가 없다면, 접근 오류 처리
exit(-1);
}
if (!vme->is_loaded) {
// 페이지가 아직 물리 메모리에 로드되지 않은 경우
vm_claim_page(vme->vaddr); // 페이지를 물리 메모리로 로드
}
}
4. 페이지 테이블 설정 (vm_claim_page)
페이지 폴트가 발생하면, 해당 가상 주소에 대한 페이지를 물리 메모리로 할당해야 합니다. 이를 위해 vm_claim_page() 함수가 호출됩니다. 이 함수는 가상 주소를 물리 메모리의 프레임에 매핑하는 역할을 합니다.
👇vm_claim_page 호출
bool vm_claim_page(void *vaddr) {
struct page *page = page_lookup(vaddr); // 해당 주소에 대한 페이지 검색
if (page == NULL) {
return false; // 페이지가 없으면 실패 처리
}
return vm_do_claim_page(page); // 페이지를 실제로 할당
}
5. 물리 메모리 프레임 할당 (vm_do_claim_page)
vm_do_claim_page() 함수는 실제로 페이지를 물리 메모리에 매핑하고, 이를 페이지 테이블에 반영하는 역할을 합니다. 이 함수에서 물리 메모리의 프레임을 할당받고, 이를 가상 주소에 매핑합니다.
👇프레임 할당 및 페이지 테이블 매핑
bool vm_do_claim_page(struct page *page) {
// 1. 새로운 물리 프레임 할당
struct frame *f = vm_get_frame();
if (f == NULL) {
PANIC("Frame allocation failed!");
}
// 2. 페이지와 프레임을 연결
f->page = page;
page->frame = f;
// 3. 페이지 테이블에 가상 주소와 물리 주소를 매핑
if (!install_page(page->vaddr, f->kva, page->writable)) {
// 매핑 실패 시 처리
PANIC("Page mapping failed!");
}
// 4. 페이지가 물리 메모리에 로드되었음을 표시
page->vme->is_loaded = true;
return true; // 성공적으로 페이지 할당
}
6. 페이지 테이블 매핑 완료
install_page() 함수는 페이지 테이블을 업데이트하여 가상 주소와 물리 메모리의 프레임을 연결합니다.
👇페이지 테이블 매핑
bool install_page(void *vaddr, void *kva, bool writable) {
// 페이지 테이블에 가상 주소(vaddr)를 커널 가상 주소(kva)에 매핑
return pagedir_set_page(thread_current()->pagedir, vaddr, kva, writable);
}
7. 물리 메모리에서 배열에 접근
이제 UVA 0x80400000에 해당하는 가상 페이지가 물리 메모리로 매핑되었으므로, 배열 arr[0]에 접근할 수 있습니다. 이때, CPU는 페이지 테이블을 사용하여 UVA에서 물리 주소로 변환을 수행합니다.