-
[정글 WEEK 12~13] Virtual Memory 큰 흐름 잡기카테고리 없음 2025. 5. 30. 14:21
Project 2까지는 프로세스 실행 시 'eager loading' 방식을 사용했다. 즉, load()하면 코드와 데이터 세그먼트를 한번에 물리 메모리 주소(pa, physical address)에 할당하고, 이를 가상 메모리 주소(va, virtual address)와 매핑했다. 하지만 이 방식은 프로세스가 물리 메모리를 아주 비효율적으로 사용한다는 단점이 있다.
프로세스 전체를 한 번에 물리 메모리에 올리면 유한한 영역을 다른 프로세스와 효율적으로 공유할 수 없다. 예를 들어, CPU를 사용하지 않고 대기 중인 프로세스가 메모리를 점유하고 있어 다른 프로세스를 실행시킬 수 없거나, 실제로는 사용되지 않는 코드나 데이터가 불필요하게 메모리에 올라가 있는 경우가 발생할 수 있다. 이런 상황은 메모리 자원의 낭비로 이어지고, 결국 시스템 전체의 효율을 떨어뜨린다.
여기서 등장한 개념이 가상 메모리이다. 가상 메모리는 전체 프로세스 주소 공간을 가상화해서 실제로 필요한 페이지만 물리 메모리에 로딩하고, 이 때 Lazy loading 전략을 사용하게 된다. Lazy Loading은 페이지에 실제 접근이 있을 때 아래와 같은 방식으로 해당 데이터를 로딩하겠다는 것이다.
1. PT(Page Table)에 특정 va와 매핑된 값으로 접근하면 당장은 매핑된 pa가 없으므로 Page Fault 발생
2. 커널이 STP(Supplemental Page Table)해당 va에 대한 보조 정보를 확인
3. 필요한 데이터를 디스크/스왑/파일시스템 등에서 로딩
4. 비로소 물리 프레임을 할당하고 해당 pa가 va와 매핑됨
5. 제어권을 다시 돌려받은 유저프로그램이 다시 va에 접근하면 이땐 PT hit!Project 3에서는 가상 메모리 기반의 Lazy loading 시스템을 직접 구현한다. Lazy loading를 지원하기 위해 supplemental page table, frame table, swap, eviction, mmap, stack growth 등의 기능도 구현해야 한다.
"lazy loading 방식으로 물리적 주소에 있는 데이터에 접근하기 위해 아래와 같은 로직을 수행한다."
1. va로 PT 조회
유저 프로그램이 PT에서 아직 pa와 매핑되지 않은 va를 참조한다.
2. PT miss → Page Fault Handler 진입
Program 2는 모든 페이지를 eager하게 palloc으로 매핑했지만, Program 3는 필요한 va에 대해서만 lazy하게 매핑하기 때문에 최초 PT 조회 시 무조건 Page Fault가 발생한다.
3. SPT에서 va에 해당하는 페이지 정보 확인
Page Fault가 발생해서 커널이 Page Fault Handler를 호출하면 Page Fault Handler는 다음 단계에서 va에 대한 정보를 얻기 위해서 SPT를 조회한다. SPT에는 va의 정보가 들어있다. 더 자세히는 각 페이지에 대한 보조 정보를 추적하는 프로세스별 자료구조로 '데이터 위치', '가상 주소 포인터', '활성 상태 여부' 등을 포함해야한다. 아래는 핀토스 supplemental_page_table 구조체 정의이다. 주석에는 멤버변수에 대한 특정 설계가 강요되지 않기 때문에 개발자에게 전적으로 설계의 자유와 책임이 달려있음이 명시되어있다.
/* Representation of current process's memory space. * We don't want to force you to obey any specific design for this struct. * All designs up to you for this. */ struct supplemental_page_table { };
4. 물리 프레임 할당palloc_get_page()로 빈 물리 페이지 프레임을 확보한다.
5. SPT의 정보에 따라서 실제 데이터 로딩
SPT에 저장된 데이터의 타입/위치 정보에 따라 해당 페이지를 찾아 프레임에 적재(load)한다. 페이지의 타입은 초기화되지 않은 페이지(uninit_page), 익명 페이지(anon_page), 파일 페이지(file_page) 중 하나이다. 초기화되지 않은 페이지는 실제 데이터를 갖지 않은 상태로 initializer에 따라 익명 페이지 또는 파일 페이지로 전환되며 적재가 이루어진다.
파일 페이지라면 file_read_at()을 통해 파일 시스템에서 데이터를 읽어오고, 익명 페이지라면 swap_in()을 통해 스왑 영역에서 데이터를 불러오거나, 초기 할당 시에는 memset(frame, 0, PGSIZE)을 통해 zero-fill 된다.
6. PA에 va → pa 매핑 정보 등록
pml4_set_page()와 같은 API를 사용해서 Page Table Entry를 갱신한다. 이때, writable 플래그에 따라 RW 권한도 설정한다.
7. 해당 va에 다시 접근 → PT hit → 정상 접근
이제는 Page Table이 갱신됐기 때문에 제어권을 넘겨받은 유저프로그램이 다시 pa에 접근할 때 PT hit돼서 Page Fault 없이 정상적으로 데이터에 접근이 가능하다.
한편, RAM에는 자주 사용될만한 데이터들이 위치해야한다.
즉, 한정된 물리 메모리(RAM) 자원을 효율적으로 활용하려면 즉시 필요하지 않은 데이터나 사용 빈도가 낮은 페이지는 메모리에서 내보내야 한다. 그리고 이것이 'eviction'의 개념이다.
이때, '페이지의 유형'에 따라 어디로 내보낼지(page-out 위치)가 달라진다. 예를 들어, 사용자 스택이나 힙이 사용하던 익명 페이지는 스왑 영역(swap partition)으로, mmap된 파일이나 실행 파일과 같은 파일 페이지는 원래의 파일 시스템으로 page-out된다. 참고로 eviction 정책이 적용된 결과가 page-out이다.이때, 페이지가 메모리에서는 수정됐지만 아직 디스크에는 반영되지 않은 'dirty' 상태이면 페이지 아웃할 때 디스크에 'write back'을 수행해 변경사항을 반영해줘야 한다. 반대로 데이터가 변경되지 않은 'clean' 상태라면 단순히 물리 메모리에서 해당 페이지를 해제하면 된다.