Project3: Virtual Memory - Management + Anonymous Page

김민호·2025년 12월 2일

pintos

목록 보기
7/9

1. 프로젝트 목표: "무한한 메모리의 환상"

  • 핵심: 물리 메모리의 크기 한계를 극복하기 위해 디스크(Swap/File) 를 메모리의 연장선으로 사용한다.
  • 원칙: Lazy Loading (게으른 로딩)
    • 당장 필요하지 않으면 메모리에 올리지 않고 장부(SPT)에만 적어둔다.
    • 실제로 필요해서 접근할 때(Page Fault) 비로소 메모리에 올린다.

2. SPT (Supplemental Page Table)

"프로세스의 모든 메모리 자원을 관리하는 절대 장부" 하드웨어 페이지 테이블(PML4)은 멍청해서 주소 변환밖에 모른다. 페이지 상태(스왑, 파일 등)를 관리할 별도의 장부가 필요하다.

  • 자료구조: 검색 속도가 빠른 Hash Table 사용 권장.
  • 위치: 프로세스(struct thread) 내부에 존재.
  • 구성요소: struct page (가상 페이지 정보를 담은 객체)
    • struct hash_elem hash_elem: 해시 테이블에 넣기 위해 반드시 추가해야 함.
    • Union: 메모리 절약을 위해 UNINIT, ANON, FILE 중 하나의 상태만 가지도록 설계됨.

SPT 메서드 구현

① 초기화 (spt_init)

  • hash_init을 호출하여 빈 테이블을 만든다.
  • 핵심: page_hash(해싱 함수)와 page_less(비교 함수)를 인자로 넘겨줘야 한다.
    • Key: 모든 기준은 가상 주소(va) 다.

② 검색 (spt_find_page)

  • 문제: hash_find는 hash_elem을 요구하는데, 우린 va밖에 없다.
  • 해결 (Dummy Page):
    1. dummy_page를 만들고 va를 채운다. (pg_round_down 필수!)
    2. hash_find에 dummy_page.hash_elem을 미끼로 던진다.
    3. 해시 함수는 오직 va만 보므로, 진짜 페이지를 찾아준다. (같은 녀석으로 인식)
  • 반환: 찾은 elem을 hash_entry로 복원해서 page 반환.

③ 삽입 (spt_insert_page)

  • 핵심: 이미 hash_insert가 중복 검사를 해주므로 별도의 검증 로직 불필요.
  • insert 성공 시 NULL 반환, 실패 시 기존 요소 반환함.

3. Frame Management (물리 메모리 관리)

"실제 땅(RAM)을 관리하고 연결하는 과정"

구조체 관계도 (3단 구조)

[ User Page (UVA) ] <---> [ Frame (KVA) ] <---> [ Physical Memory (PA) ]

  • UVA: 사용자가 쓰는 가짜 주소. 권한이 없어서 물리 메모리에 직접 못 감.
  • KVA: 커널이 쓰는 마스터키. 물리 메모리와 1:1 직통 연결됨.
  • Page는 Frame을 가진다: page는 신청서, frame은 등기부. 둘을 연결해야 메모리 사용 가능.

Frame 메서드 구현

  1. vm_get_frame (땅 확보):

    • palloc_get_page(PAL_USER): 유저 풀에서 4KB 땅(KVA)을 받아온다.
    • malloc: 커널 풀에서 struct frame 서류를 만든다.
    • 둘을 연결(frame->kva = kva)하고 반환.
  2. vm_do_claim_page (연결):

    • vm_get_frame 호출.
    • page <-> frame 서로 포인터 연결.
    • pml4_set_page: 하드웨어(MMU)에 "이 가상 주소는 저 물리 주소다"라고 등록.
    • swap_in: 데이터 로딩 시작.
  3. vm_claim_page (인터페이스):

    • va로 페이지 찾아서 vm_do_claim_page에 넘기는 결정자 역할.

4. Lazy Loading의 전체 시나리오 (핵심 흐름)

"쪽지(Aux)를 써두고, 나중에 펴보고 심부름시킨다."

Load (프로그램 시작)

데이터를 메모리에 올리지 않고 쪽지(aux)만 써둔다.

  • 함수: load_segment -> vm_alloc_page_with_initializer
  • 동작:
    1. 파일을 4KB씩 쪼갠다.
    2. 쪽지(aux) 작성: "나중에 이 파일의 이 위치 읽어라."
    3. 페이지 생성: VM_UNINIT 상태로 만들고, 쪽지와 작업반장(lazy_load_segment)을 넣어둠.
    4. SPT 등록: 메모리는 비어있지만 장부에는 가득 참.

Page Fault (실행 중)

사용자가 주소를 건드려서 에러가 난다.

  • 함수: vm_try_handle_fault -> vm_do_claim_page -> swap_in
  • 동작:
    1. swap_in이 호출되면 uninit_initialize가 실행된다.
    2. 신분 세탁: anon_initializer 등을 호출해 VM_ANON 등으로 변신.
    3. 데이터 로딩: 아까 넣어둔 작업반장(lazy_load_segment) 호출.

Lazy Load (진짜 로딩)

  • 함수: lazy_load_segment
  • 동작:
    1. 쪽지(aux)를 펼쳐본다.
    2. file_read로 진짜 데이터를 읽어서 kva에 넣는다.
    3. 남은 공간은 memset(0)으로 청소한다 (보안!).

5. Stack & Memory Mapping Details

Stack (스택) 초기화 (setup_stack)

  • 특징: 파일 족보가 없다. 그냥 빈 메모리(VM_ANON)다.
  • Eager Loading: 첫 페이지는 인자 전달(argc, argv)을 위해 당장 써야 하므로 바로 할당(claim)한다.
  • 구현:
    1. vm_alloc_page(VM_ANON | VM_MARKER_0, ...) (예약)
    2. vm_claim_page(...) (즉시 할당)
    3. rsp 설정 (USER_STACK)

6. Process Lifecycle (Copy & Kill)

Fork (spt_copy)

부모의 SPT를 hash_iterator로 돌면서 복사한다. 얕은 복사는 절대 금지!

  • VM_UNINIT: 아직 로딩 안 됨 -> 계획(Initializer, Aux)만 복사.
  • VM_ANON: 이미 로딩 됨 -> 물리 메모리 할당(claim) + 내용 복사(memcpy).
    • struct page를 복사하는 게 아니라, 내용물(4KB)을 복사해야 함!

Exit (spt_kill)

  • hash_clear로 장부를 비우면서 destroy 호출 -> 자원 해제.
profile
개발자를 꿈꾸고 있어요

0개의 댓글