Pintos VM Project - lazy_load_segment & fork-read 디버그

2025. 6. 5. 20:40개발

문제 현상

  • fork-read 테스트에서 Kernel panic 발생
  • Call Stack 상에서 free()lazy_load_segment()kill()debug_panic() 흐름
  • 원인 파악을 위해 lazy load 및 페이지 테이블 초기화 흐름 분석 필요

전체 함수 흐름

process_exec()
└─ load()
   └─ load_segment()                    // VM이 켜졌다면 lazy load 등록만 함
      └─ vm_alloc_page_with_initializer()
         └─ uninit_new()                // uninit_page 구성 및 aux 저장

[page fault 발생 시 → vm_try_handle_fault() → vm_do_claim_page() 호출됨]
└─ vm_do_claim_page()
   ├─ vm_get_frame()
   ├─ pml4_set_page()
   └─ swap_in()
      └─ uninit_initialize()
         ├─ page_initializer()         // 실제 타입에 따라 file_initializer 등 호출
         └─ init(aux)                  // 이게 lazy_load_segment()
  

문제 원인 분석

  • load_segment()에서 lazy_load_arg 구조체 auxmalloc()으로 생성하여 uninit_page에 저장
  • fork 시 spt가 shallow copy되므로 aux도 공유
  • 만약 lazy_load_segment()에서 free(aux)를 하면, 자식이 먼저 실행되며 해당 aux를 free한 뒤
  • 부모가 같은 aux를 다시 free()Double Free → Kernel Panic

 해결 방법

  • lazy_load_segment()에서 aux를 해제하지 않는다
  • 대신, destroy(page)uninit_destroy()free(aux)에서 정상적으로 해제
  • 이는 spt_destroy()가 호출될 때 수행됨

 테스트와 결과

  • lazy_load_segment()에서 free(aux)를 포함할 경우 → fork-read에서 커널 패닉 발생
  • 이를 제거하면 테스트 정상 통과

 추가 개념 정리

  • aux: lazy load용 메타데이터 구조체 (file, offset, read_bytes 등 저장)
  • lazy_load_segment(): 실제로 첫 페이지 접근 시 파일에서 데이터를 로드
  • uninit_page: lazy load용으로 초기화된 페이지 구조체
  • destroy(): 페이지 해제 시 호출되며, 내부에서 aux 정리 포함

공부해볼 것

  • vm_alloc_page_with_initializer()uninit_new()의 구조 이해
  • vm_try_handle_fault()vm_do_claim_page()uninit_initialize() 전체 흐름
  • fork 시 메모리 복사 방식 (shallow vs deep copy)
  • Double Free가 발생하는 조건

핵심 정리

  • aux를 언제 free할 것인가?destroy()에서
  • fork 시 왜 문제가 되는가? → aux는 공유됨. premature free는 double free를 유발
  • load_segment()는 무엇을 하나? → 실제 데이터 로딩이 아닌, lazy load 등록만 수행
  • lazy_load_segment는 언제 호출되나? → 페이지 폴트가 발생했을 때, 실제로 로딩 수행