이번 글에서는 PintOS Project 3의 Swap in/out 파트 중,
anon과 관련된 구현 내용을 정리합니다.
KAIST 공식 문서의 중 아직 남은 함수들인 다음 네 가지를 중점적으로 다룹니다:
- vm_anon_init(void)
- anon_initializer(struct page *page, enum vm_type type, void *kva)
- anon_swap_out(struct page *page)
- anon_swap_in(struct page *page, void *kva)
Anonymous Page란?
Anonymous Page는 특정한 파일에 연결되지 않은 페이지입니다. 예를 들어, 프로그램 실행 중 malloc이나 brk를 통해 동적으로 할당되는 메모리 영역은 Anonymous Page에 해당합니다. 이 페이지들은 파일 백업이 없기 때문에, 물리 메모리에서 제거될 경우 이를 보존할 다른 저장소가 필요합니다.
이때 사용하는 것이 Swap Disk입니다. 익명 페이지는 메모리 부족 등의 이유로 스왑 아웃될 수 있으며, 이후 다시 필요할 때 스왑 안됩니다. 이때 실제 데이터를 임시로 저장하고 불러오는 장치가 바로 swap disk입니다.
Swap In / Swap Out이란?
- Swap Out: 익명 페이지의 데이터가 현재 물리 메모리에 있지만, 메모리 공간이 부족한 경우 해당 페이지를 swap disk로 옮깁니다. 이 과정을 swap out이라고 합니다.
- Swap In: 스왑 아웃된 페이지가 다시 필요해질 경우, swap disk로부터 해당 데이터를 읽어와 메모리에 복원하는 과정입니다.
Anonymous Page Swap In/Out - Control Flow (전체 흐름도)
[페이지가 메모리 부족 등으로 evict 대상이 됨]
│
▼
swap_out(page)
│
▼
[page->operations->swap_out(page) 호출]
│
▼
anon_swap_out(struct page *page)
│
├─ 빈 swap 슬롯 찾기 (bitmap 등)
├─ page->frame->kva 에서 8개 sector 단위로 disk_write()
├─ page->anon.swap_slot = 할당된 slot index
└─ page->frame 해제 (pml4_clear 등 포함)
(결과: 페이지가 스왑 디스크에 저장되고 메모리에서 제거됨)
────────────────────────────────────────────────────────────
[해당 페이지에 접근 발생 → page fault 발생]
│
▼
handle_page_fault() → vm_try_handle_fault()
│
▼
spt_find_page(spt, addr) → page 구조체 반환
│
▼
page->operations->swap_in(page, kva)
│
▼
anon_swap_in(struct page *page, void *kva)
│
├─ page->anon.swap_slot 위치에서 8개 sector disk_read()
├─ 데이터를 kva로 복사
├─ page->anon.swap_slot = -1 (또는 INVALID)
└─ swap bitmap에서 해당 slot free 처리
(결과: 페이지가 다시 메모리에 로드되어 정상 실행)
1. vm_anon_init() - Anonymous Page Swapping 서브시스템 초기화 함수
// .../vm/anon.c
/* Initialize the data for anonymous pages */
void vm_anon_init(void)
{
/* 스왑 영역으로 사용할 디스크를 가져옵니다.
디스크 번호(1, 1)은 PintOS에서 일반적으로 스왑 디스크로 설정된 위치입니다.
*/
swap_disk = disk_get(1, 1);
// 예외 처리 : swap_disk가 없을 경우 커널 패닉 발생
if (swap_disk == NULL)
{
PANIC("CAN'T FIND SWAP DISK!");
}
/** TODO: bitmap 자료구조로 스왑 테이블 만들기
스왑 테이블은 각 스왑 슬록(swap slot)의 사용 여부를 추적하기 위한 비트맵
- 스왑 슬롯 : 메모리의 한 페이지를 디스크에 저장할 수 있는 최소 단위(1 page = PGSIZE)
- 각 스왑 슬롯은 여러 개의 디스크 섹터(sector)로 구성
따라서 스왑 슬롯의 개수 = 전체 디스크 섹터 수 / 한 페이지를 구성하는 섹터 수
- 디스크 전체 섹터 수 : disk_size(swap_disk)
- 한 페이지당 섹터 수 : PGSIZE / DISK_SECTOR_SIZE
이 값을 기반으로 비트맵을 생성
- bitmap의 각 비트는 하나의 스왑 슬롯을 의미하며, 0이면 비어있고 1이면 사용중
*/
swap_table = bitmap_create(disk_size(swap_disk) / (PGSIZE / DISK_SECTOR_SIZE));
}
1.1. vm_anon_init() 핵심 요약
- 스왑 디스크(swap_disk)는 disk_get(1,1)을 통해 할당되며, 이는 일반적으로 PintOS에서 스왑 용도로 사용되는 디스크입니다.
- 스왑 테이블(swap_table)은 비트맵(bitmap)으로 구성되며, 각 비트는 하나의 스왑 슬롯 사용 여부를 나타냅니다.
- 하나의 스왑 슬롯은 한 페이지(PGSIZE)를 저장할 수 있으며, 이는 여러 디스크 섹터(DISK_SECTOR_SIZE)로 이루어져 있으므로 전체 슬롯 수 계산 시 나눗셈이 필요합니다.
1.2. vm_anon_init()을 이렇게 구현한 이유
- swap_disk를 설정하지 않으면 스왑 기능이 아예 작동하지 않기 때문에 커널이 부팅될 때 반드시 초기화해야 합니다.
- 비트맵을 사용하는 이유는 간단하고 빠르게 사용 가능한 스왑 슬롯을 추적할 수 있기 때문입니다.
예를 들어, bitmap_scan_and_flip()을 사용하여 빠르게 빈 슬롯을 찾고 할당할 수 있습니다. - 스왑 슬롯을 정해진 크기(PGSIZE)로 고정하여 관리하는 방식은 구현이 단순하며, 페이지 단위로 스왑 인/아웃할 때 일관성을 제공합니다.
2. anon_initializer() - Anonymous Page용 초기화 함수
//.../vm/anon.c
/* Initialize the file mapping */
bool anon_initializer(struct page *page, enum vm_type type, void *kva)
{
// 예외 처리 → 들어온 page가 null일 경우
if (page == NULL)
{
return false;
}
/* 이 페이지는 anonymous 페이지이므로, anon_ops로 설정
anon_ops는 스왑 인/아웃 등을 포함한 함수 포인터 구조체
*/
page->operations = &anon_ops;
/* uninit을 anon으로 변환 */
struct anon_page *anon_page = &page->anon; // page->anon은 포인터가 아니라 구조체 자체여서 항상 유효한 주소를 반환함
/* swap index 초기화
-1은 아직 스왑 아웃된 적이 없다는 것을 의미
*/
anon_page->swap_idx = -1;
/* 페이지의 물리 주소(kva)가 유효하며,
해당 페이지의 frame이 1개 이하로만 참조되고 있을 경우
새로 할당된 물리 페이지라고 간주하고 초기화함
*/
if (kva != NULL && page->frame->ref_cnt <= 1)
{
// 물리 페이지 전체를 0으로 초기화(보안 및 예측 가능한 동작 보장)
memset(kva, 0, PGSIZE);
}
return true;
}
2.1. anon_initializer() 핵심 요약
- 이 함수는 uninit 상태의 페이지가 Anonymous 타입으로 전환될 때 호출됩니다.
- page->operations를 anon_ops로 설정하여 해당 페이지의 스왑 인/아웃 함수들을 지정합니다.
- 내부 구조체인 anon_page를 초기화하며, 특히 swap_idx를 -1로 설정해 아직 스왑 된 적 없음을 표시합니다.
- 추가적으로, 물리 주소(kva)가 유효하고 프레임 참조 카운트가 1 이하일 경우, 해당 물리 페이지를 memset으로 0으로 초기화합니다.
2.2. anon_initializer()를 이렇게 구현한 이유
- 함수 포인터(operations) 설정
익명 페이지는 파일과 연결된 페이지(file-backed)와는 다르게, 디스크 백업이 없는 순수 메모리 페이지입니다. 따라서 해당 페이지에 스왑 인/아웃 기능을 연결하기 위해 anon_ops를 지정해 줍니다. - swap_idx를 -1로 초기화하는 이유
해당 페이지가 아직 디스크에 스왑 아웃된 적이 없음을 명시적으로 표시함으로써, 이후 스왑 인/아웃 시 혼동을 방지할 수 있습니다. - 물리 페이지 초기화의 이유
새로 할당된 물리 페이지는 과거의 데이터가 남아 있을 수 있으므로, 이를 0으로 초기화하여 보안을 강화하고 예측 가능한 동작을 보장합니다.
단, 공유된 프레임이거나 재사용되는 경우는 제외해야 하므로 ref_cnt <= 1 조건을 둡니다
3. anon_swap_in() – Anonymous Page를 디스크에서 메모리로 복원하는 함수
//.../vm/anon.c
/* 스왑 디스크에서 내용을 읽어와 페이지를 스왑인합니다. */
static bool
anon_swap_in(struct page *page, void *kva)
{
struct anon_page *anon_page = &page->anon;
int swap_idx = anon_page->swap_idx;
// 예외 처리 → swap_idx가 -1이면 페이지가 스왑아웃된 적이 없거나 이미 복구되었으므로 스왑 인 생략
if (swap_idx < 0)
{
return false;
}
// disk_read에서 사용할 버퍼
// void *buffer[PGSIZE];
/** TODO: 페이지 스왑 인
* disk_read를 데이터를 읽고 kva에 데이터 복사
* swap_idx를 -1로 바꿔주어야 함
* 프레임 테이블에 해당 프레임 넣어주기
* 프레임하고 페이지 매핑해주기
*/
// 한 섹터는 512바이트이고, 한 페이지는 4KB(4096바이트)이므로
// 총 8개의 섹터를 순차적으로 읽어야 전체 페이지 데이터를 복원할 수 있음
// swap_idx는 스왑 테이블 상의 페이지 단위 인덱스를 의미하며,
// 실제 섹터 번호는 swap_idx * 8부터 시작함
for (int i = 0; i < 8; i++)
{
disk_read(swap_disk, // 스왑 디스크에서 데이터를 읽어옴
(swap_idx * 8) + i, // 8개의 연속된 섹터에 페이지가 저장되어 있으므로, i를 더해가며 읽음
kva + (DISK_SECTOR_SIZE * i)); // 읽어온 데이터를 커널 가상 주소 kva에 512B 단위로 복사
}
// 스왑 테이블에서 해당 스왑 슬롯을 비어있다고 표시 (해당 슬롯 재사용 가능하도록)
bitmap_reset(swap_table, swap_idx);
// 페이지가 더 이상 스왑 영역에 존재하지 않음을 나타내기 위해 swap_idx를 -1로 초기화
anon_page->swap_idx = -1;
return true;
}
3.1. anon_swap_in() 핵심 요약
anon_swap_in() 함수는 Anonymous Page가 디스크에 스왑 된 이후, 다시 메모리로 불러올 때 사용하는 함수입니다.
swap_idx를 이용해 디스크에서 데이터를 읽어와 kva 주소로 복사하며, 스왑 슬롯의 사용 상태를 해제하고 페이지의 swap_idx를 초기화합니다.
3.2. anon_swap_in()을 이렇게 구현한 이유
- 스왑 디스크에서 페이지 단위로 읽기
PintOS에서는 한 페이지가 디스크 상의 **8개의 섹터(=512B × 8 = 4096B)**에 저장됩니다.
따라서 disk_read()를 8번 호출하여 스왑 된 데이터를 전체 읽어와야 합니다. - swap_idx를 사용한 정확한 위치 계산
swap_idx는 스왑 슬롯 단위 인덱스이며, 실제 섹터 번호는 swap_idx * 8 + i로 계산됩니다.
이를 통해 정확히 해당 페이지의 저장 위치를 추적하고 복구할 수 있습니다. - 비트맵 해제 및 swap_idx 초기화
해당 스왑 슬롯이 다시 사용될 수 있도록 bitmap_reset()을 호출해 비트를 0으로 바꾸고,
swap_idx를 -1로 설정하여 "스왑 아웃 상태가 아님"을 표시합니다.
4. anon_swap_out() – Anonymous Page를 디스크로 스왑 아웃하는 함수
//.../vm/anon.c
/* 페이지의 내용을 스왑 디스크에 기록하여 스왑아웃합니다. */
static bool
anon_swap_out(struct page *page)
{
// 예외 처리: page가 NULL이면 실패
if (page == NULL)
{
return false;
}
struct anon_page *anon_page = &page->anon;
// 비어있는 스왑 슬롯을 비트맵에서 검색하고 할당 (false인 비트를 true로 뒤집음)
size_t swap_idx = bitmap_scan_and_flip(swap_table, 0, 1, false);
// 스왑 슬롯 할당 실패 시 (비트맵 에러)
if (swap_idx == BITMAP_ERROR)
{
ASSERT(bitmap_test(swap_table, swap_idx) == false); // 디버깅용 확인
return false;
}
// 페이지 전체(4KB)를 스왑 디스크에 저장 (512B * 8 = 4096B)
for (int i = 0; i < 8; i++)
{
disk_write(
swap_disk, // 스왑 디스크에
(swap_idx * 8) + i, // 8개의 섹터에 순차 저장
page->frame->kva + (DISK_SECTOR_SIZE * i) // 현재 페이지의 실제 데이터
);
}
// 페이지와 프레임 간의 연결을 끊어 더 이상 메모리에 존재하지 않도록 함
page->frame->page = NULL;
page->frame = NULL;
// anon_page에 swap_idx 저장 → 나중에 다시 swap_in할 때 사용
anon_page->swap_idx = swap_idx;
return true;
}
4.1. anon_swap_out() 핵심 요약
anon_swap_out()은 Anonymous Page를 메모리에서 제거하고, 해당 내용을 디스크의 스왑 영역으로 내보내는 함수입니다.
스왑 디스크에 저장된 페이지는 추후 필요시 anon_swap_in()을 통해 다시 메모리로 불러올 수 있습니다.
4.2. anon_swap_out()를 이렇게 구현한 이유
- 페이지 내용을 디스크에 기록하기 위한 스왑 아웃
Anonymous Page를 메모리에서 디스크로 내보내기 위해, 스왑 디스크의 빈 공간을 찾아 disk_write()를 통해 데이터를 저장합니다. 이 과정을 통해 메모리를 효율적으로 관리하고, 이후 필요시 다시 불러올 수 있게 됩니다. - 비어 있는 스왑 슬롯 검색: bitmap_scan_and_flip()
- 왜?: 스왑 디스크는 페이지 단위로 관리되며, 각 슬롯의 사용 여부는 bitmap을 통해 추적합니다.
- bitmap_scan_and_flip(swap_table, 0, 1, false)는 **false(=비어있는 슬롯)**를 찾아 true로 설정합니다.
- 이렇게 하면 중복 없는 슬롯 할당이 가능하고, 이후 복구 시 정확한 위치 추적이 용이합니다.
- 예외 처리: BITMAP_ERROR 검사
- bitmap_scan_and_flip()의 반환값이 BITMAP_ERROR라면 스왑 공간이 부족한 것이므로, 즉시 false를 반환해 에러를 처리합니다.
- ASSERT(bitmap_test(...))는 디버깅 도중 논리 오류 방지를 위해 작성한 방어 코드입니다.
- disk_write() 반복 호출: 페이지 전체 저장
- 한 페이지는 4KB(=4096B), 디스크의 한 섹터는 512B이므로, 총 8개의 연속된 섹터에 나눠 저장해야 합니다.
- 따라서 for (int i = 0; i < 8; i++) 반복문을 통해, disk_write()를 8번 호출하여 512B씩 데이터를 저장합니다.
- 저장 위치는 swap_idx * 8 + i로 계산하여, 해당 슬롯의 정확한 물리 주소를 추적합니다.
'PintOS > Project 3 : VIRTUAL MEMORY' 카테고리의 다른 글
| [PintOS] Project 3 : VM - Swap In/out(File-Mapped Page) (0) | 2025.06.11 |
|---|---|
| [PintOS] Project 3 : VM - Memory Mapped Files - File-Backed 페이지 구성(with lazy loading) (0) | 2025.06.11 |
| [PintOS] Project 3 : VM - Memory Mapped Files - mmap & munmap 구현(with lazy loading) (0) | 2025.06.11 |
| [PintOS] Project 3 : VM - Stack Growth (7) | 2025.06.09 |
| [PintOS] Project 3 : VM - Anonymous Page (1) | 2025.06.07 |