이번 글에서는 PintOS의 Virtual Memory 프로젝트에서 vm_get_frame(), vm_claim_page(), vm_do_claim_page() 함수가 어떤 역할을 수행하는지 정리합니다. Supplement Page Table(SPT)을 구성했다면, 이제 물리 메모리 프레임을 실제로 할당하고, 이 프레임을 페이지에 연결하며, 페이지 테이블(MMU)에 매핑하는 단계입니다.
1. vm_get_frame() : 사용자 공간에서 물리 프레임을 하나 할당
/* palloc()을 사용하여 프레임을 할당합니다.
* 사용 가능한 페이지가 없으면 페이지를 교체(evict)하여 반환합니다.
* 이 함수는 항상 유효한 주소를 반환합니다. 즉, 사용자 풀 메모리가 가득 차면,
* 이 함수는 프레임을 교체하여 사용 가능한 메모리 공간을 확보합니다.*/
static struct frame *
vm_get_frame(void)
{
/* 1. 새로운 frame 구조체를 메모리에 할당
이는 물리 페이지의 메타데이터를 저장할 공간
반드시 free해라 뒤지기싫으면..
*/
struct frame *frame = malloc(sizeof(struct frame));
// 예외 처리 → 할당 실패시 시스템 중단
ASSERT(frame != NULL);
/* 2. 실제 물리 페이지(4KB) 할당 시도
PAL_USER : 사용자 프로세스용 메모리 풀에서 할당
PAL_ZERO : 할당된 페이지를 0으로 초기화
*/
frame->kva = palloc_get_page(PAL_USER | PAL_ZERO);
/* 3. 메모리 부족 상황 처리
사용 가능한 물리 페이지가 없는 경우 처리
*/
if (frame->kva == NULL)
{
/* 3.1. 희생자(victim) 프레인 선택 및 교체
vm_evict_frame()은 페이지 교체 알고리즘(처음: FIFO, 지금: Clock)을 사용하여
교체할 프레임을 선택, 해당 페이지를 디스코로 내보냅니다.
*/
struct frame *victim1 = vm_evict_frame();
// 예외 처리 → 교체 실패시 시스템 중단
ASSERT(victim1 != NULL);
/* 3.2. 희생자의 물리 페이지를 재활용
교체된 프레임의 물리 주소를 새로운 프레임이 사용
*/
frame->kva = victim1->kva; // victim의 물리 페이지를 재활용
/* 3.3. 희생자 프레임 구조체 해제
물리 페이지는 재활용하지만, 메타데이터는 새로 만듦
*/
free(victim1);
}
/* 4. 새로운 프레임 초기화
아직 어떤 가상 페이지와도 연결되지 않은 상태
*/
frame->page = NULL; // 연결된 가상 페이지 X
frame->ref_cnt = 1; // 참조 카운터 초기화(COW extra 과제 용)
/* 5. 프레임 테이블에 등록
시스템이 이 프레임을 추적할 수 있도록 전역 프레임 테이블에 추가
*/
frame_table_insert(&frame->elem);
/* 예외 처리 → 프레임이 올바르게 초기화 되었나? */
ASSERT(frame->page == NULL);
/* 6. 할당된 프레임 반환
이제 이 프레임은 가상 페이지와 연결된 준비가 완료되었습니다.
*/
return frame;
}
1.1 핵심 요약
- palloc_get_page()를 사용하여 사용자 공간에서 물리 프레임을 하나 할당합니다.
- 만약 할당 실패 시, 프레임 교체(victim frame)를 통해 확보합니다. (swap out은 나중에 구현 예정)
- 할당한 프레임은 전역 frame_table에 등록됩니다.
1.2 참고 : 왜 프레임을 꼭 여기서 할당해야 하나요?
→ 가상 페이지는 반드시 물리 프레임과 매핑되어야 MMU가 접근할 수 있습니다. 따라서 SPT에만 페이지 정보를 기록하는 것으로는 부족하고, 프레임까지 실제로 확보해야 해당 주소를 사용할 수 있습니다.
2. vm_claim_page() : 가상 주소에 해당하는 페이지를 요구 (Page Fault 시 호출)
/* VA에 할당된 페이지를 요구합니다.
이 함수는 Page Fault 발생 시 호출되어 가상 페이지를 물리 메모리에 로드합니다.
*/
bool vm_claim_page(void *va UNUSED)
{
/* 1. 가상 주소로 SPT에서 해당 페이지 구조체 찾기 */
struct page *page = spt_find_page(&thread_current()->spt, // 현재 스레드의 보조 페이지 테이블
va); // 요청된 가상 주소(e.g. 0x400000000(예시임 너무 신경 ㄴㄴ), 스택 주소, 힙 주소 등등)
/* 2. 예외 처리 → 페이지 존재 여부 검증
page == NULL인 case :
- 할당되지 않은 메모리 영역에 접근 (segmentation fault 상황)
- 잘못된 가상 주소 접근
- 아직 vm_alloc_page로 생성되지 않은 페이지
*/
if (page == NULL)
{
return false; // 실패 : 유효하지 않은 페이지 요청
}
/* 3. 실제 페이지 클레임 수행*/
return vm_do_claim_page(page);
}
2.1 핵심 요약
- Page Fault가 발생하면 호출되어 해당 가상 주소의 struct page를 찾고,
- vm_do_claim_page()를 통해 실제 물리 메모리 프레임을 연결하고,
- MMU(Page Table)에 매핑까지 수행합니다
2.2 참고 : 왜 이 함수가 중요한가요?
→ SPT에 있는 가상 주소 정보만으로는 프로그램이 접근할 수 없습니다.
실제 프로그램이 해당 주소를 접근하려면, 해당 주소가 물리 메모리와 연결되어 있어야 합니다.
즉, 이 함수는 SPT에 존재하는 페이지 정보를 진짜 사용할 수 있도록 활성화해 주는 역할을 합니다.
3. vm_do_claim_page() : 페이지와 프레임 연결 + MMU 매핑 + 페이지 데이터 로딩
/* PAGE를 요구하고 mmu를 설정합니다.*/
static bool
vm_do_claim_page(struct page *page)
{
// 1. 물리 프레임 할당
struct frame *frame = vm_get_frame();
/* Set links */
/* 2. 양방향 링크 설정 (페이지 ← - → 프레임 연결)
가상 페이지와 물리 프레임 간의 관계를 설정
*/
frame->page = page;
page->frame = frame;
/* TODO: Insert page table entry to map page's VA to frame's PA. */
// 3. 페이지 테이블 엔트리 설정(MMU 매핑)
if (!pml4_set_page(thread_current()->pml4, // 현재 스레드 페이지 테이블
page->va, // 가상 주소
frame->kva, // 물리 주소 (커널 가상 주소)
page->writable)) // 쓰기 권한
{
/* 매핑 실패 처리
실패 case :
- 메모리 부족
- 이미 매핑된 주소
- 잘못된 권한
등등 더 자세한 설명은 생략한다. 디버깅해서 찾아보슈
*/
return false;
}
// 4. 실제 페이지 내용 로딩
return swap_in(page, frame->kva);
}
3.1 핵심 요약
- vm_get_frame()을 통해 물리 메모리 프레임을 하나 확보하고,
- 해당 프레임과 페이지 구조체 간 양방향 연결을 설정합니다.
- 이후, pml4_set_page()로 MMU(Page Table)에 VA → PA 매핑을 설정합니다.
- 마지막으로 swap_in()을 통해 페이지 내용을 로드합니다. (e.g. lazy load, swap 영역에서 복구 등)
3.2 참고 : swap_in()이란?
→ 현재는 아직 lazy load나 swap 기능이 완벽히 구현되지 않았더라도, swap_in()은 다양한 페이지 초기화 전략(예: 파일에서 읽어오기, zero 페이지 생성 등)을 처리할 수 있도록 추상화된 함수입니다.
4. 전체 Control Flow
[Page Fault 발생]
│
▼
[vm_claim_page(va)]
│
├─ SPT에서 해당 va에 대응하는 페이지 찾기
│ └─ 실패 → return false
│
▼
[vm_do_claim_page(page)]
│
├─ vm_get_frame() 호출 → 물리 프레임 확보
│ │
│ ├─ palloc_get_page() 시도
│ │ └─ 성공 → 프레임 반환
│ │
│ └─ 실패 → vm_evict_frame()으로 victim 선택
│ └─ victim 프레임의 kva 재사용 + 구조체 해제
│
├─ frame.page ←→ page.frame 양방향 연결
│
├─ pml4_set_page()로 MMU에 VA → PA 매핑
│ └─ 실패 → return false
│
▼
[swap_in(page, frame->kva)]
│
└─ 해당 페이지의 데이터를 물리 메모리로 복원
(e.g. lazy load, swap 영역 등)
4.1 시각화 요약 (흐름 파악용)
[Page Fault]
│
▼
[SPT에서 페이지 찾기]
│
▼
[프레임 확보 (get_frame)]
│
▼
[프레임과 페이지 연결]
│
▼
[MMU 매핑 설정]
│
▼
[swap_in → 실제 데이터 로드]
5. 마무리 - 왜 이렇게 구현해야 하나?

농담입니다~ PintOS는 몸에 해롭다.
| 목적 | 이유 |
| 프레임 할당 | 물리 메모리 공간이 필요하기 때문 |
| SPT ↔ 프레임 연결 | 커널이 어떤 페이지가 어떤 프레임과 연결되었는지 추적 |
| MMU 매핑 설정(pml4_set_page) | CPU가 해당 가상 주소를 올바른 물리 주소로 변환할 수 있도록 하기 위함 |
| swap_in | 해당 주소의 내용을 실제 메모리에 올리기 위해 |
'PintOS > Project 3 : VIRTUAL MEMORY' 카테고리의 다른 글
| [PintOS] Project 3 : VM - Stack Growth (7) | 2025.06.09 |
|---|---|
| [PintOS] Project 3 : VM - Anonymous Page (1) | 2025.06.07 |
| [PintOS] Project 3 : VM - Memory Management - SPT(Supplemental Page Table) 함수 분석 및 구현 흐름 정리 (1) | 2025.06.05 |
| [PintOS] Proejct 3 : VM Memory Management - frame, page, SPT, frame table (3) | 2025.06.05 |
| [PintOS] Project 3 : VM 들어가기 앞서 알아야 할 지연 로딩(Lazy Loading) (0) | 2025.06.03 |