PintOS/Project 3 : VIRTUAL MEMORY

[PintOS] Project 3 : VM - Memory Management - Frame alloc and Page Mapping

넌뭐가그렇게중요해 2025. 6. 5. 17:50

이번 글에서는 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 해당 주소의 내용을 실제 메모리에 올리기 위해