메모리 스와핑 : 메모리 회수기법
누가 ?  : 운영체제 의해 이루어짐
언제 ?  : 유저 프로그램이 메모리 할당을 요청했을 때
무엇을 ? : 사용되지 않는 메모리 프레임들을 디스크로 스왑 아웃!
어떻게 ? : 운영체제가 감지하면 swap 디스크로 퇴거(evict)시킬 페이지를 골라서 디스크에 복사함
왜 ?  : 메인 메모리의 프레임들이 모두 할당되어, 시스템이 유저 프로그램의 메모리 할당 요청을 처리할 수 없기 때문에
PINTOS 에서 Disk는 sector라는 최소단위로 data를 관리하며 sector의 크기는 512bytes 이다.
따라서 한개의 page를 저장하기 위해 한개의 swap slot은 8개의 sector를 사용한다.
swap out시 data를 임시 저장하기 위한 공간* swap in 전까지 해당 data는 안전하게 유지
Sector : disk에서 data 를 관리하는 최소단위로, Pintos 에서는 512 bytes 이다.
Swap Slot : page를 저장하기 위해 4096 bytes 씩 나누어 관리할 수 있다.
한개의 Page 를 위해, 8개의 Sector 필요
Swap Table : Swap slot의 할당여부를 관리 (1=할당됨 / 0=할당가능)
파일로부터 매핑되지 않은 페이지로서 커널 로부터 할당 된 페이지이다.
디스크에 있던 프로그램이 실행될 때 스택 과 힙 섹션은 익명 페이지로 메모리에 할당 된다.
스왑 디스크라는 임시 백업 저장소가 필요스왑 디스크 설정 ( 사용가능/불가능 영역 관리 , 영역=PGSIZE 단위)
void
vm_anon_init (void) {
	// 1:1 - 스왑(교환) 영역
	swap_disk = disk_get(1,1);
	/* sectors / 8 = slot index */
	size_t bitcnt = disk_size(swap_disk)/SECTORS_IN_PAGE;
	swap_table = bitmap_create(bitcnt);
}
스와핑을 지원
bool
anon_initializer (struct page *page, enum vm_type type, void *kva) {
	/* Set up the handler */
	page->operations = &anon_ops;
	struct anon_page *anon_page = &page->anon;
	anon_page->swap_index = -1;
	return true;
}
스왑 디스크 데이터 내용을 읽어서 익명 페이지를(디스크에서 메모리로) swap in
static bool
anon_swap_in (struct page *page, void *kva) {
	struct anon_page *anon_page = &page->anon;
		size_t sec_no = anon_page->swap_index;
	//  sec_no 그룹에 비트가 할당되어 있지 않다면 return (디스크에 write 되지 않았음)
    if (bitmap_test(swap_table, sec_no) == false) 
        return false;
	// swap disk 에 있는 ( 8배수 SECTOR 인덱스 + i ) 안의 정보를 (커널가상주소 + 512 * i) 로 읽기
    for (int i = 0; i < SECTORS_IN_PAGE; ++i) {
        disk_read(swap_disk, sec_no * SECTORS_IN_PAGE + i, kva + DISK_SECTOR_SIZE * i);
    }
    bitmap_set(swap_table, sec_no, false);
    
    return true;
}
Evit 될 페이지를 찾아 swap out!
1. 메모리에서 디스크로 내용을 복사
2. 익명 페이지를 스왑 디스크로 교체
3. backing store 할 공간이 없기 때문에 Swap disk 사용
스왑 테이블을 사용하여 디스크에서 사용 가능한 스왑 슬롯 찾기
- 다음 데이터 페이지를 슬롯에 복사
 - 데이터의 위치는 페이지 구조체에 저장
 - 디스크에 사용 가능한 슬롯이 더 이상 없으면 커널 패닉이 발생할 수 있음
 
static bool
anon_swap_out (struct page *page) {
	struct anon_page *anon_page = &page->anon;
	// page = evict page 에 대한 정보를 swap disk 에 저장
	// Swap out : 유저 프로그램이 메모리 요청했지만, 다 차 있기 때문에 일부 페이지를 퇴출한다. 퇴출된 페이지는 메모리에서 삭제되고 디스크에 복사된다.
	/* 1. 페이지를 할당받을 수 있는 swap slot 하나를 찾기 */
	//	  Swap table 에서 0번째부터 false값을 갖는 비트를 1개 찾기 (false=할당가능)
	size_t sec_no = bitmap_scan(swap_table, 0, 1, false);
	if(sec_no == BITMAP_ERROR)
		return false;
	/* 2. 디스크에 페이지를 복사하기 */
	//    sector(512 bytes) * 8 = slot = 1 page
	// SEC_NO를 버퍼로 디스크에 기록 - 버퍼는 512 바이트를 포함해야 합니다
	for(int i = 0; i < SECTORS_IN_PAGE; i++){
		disk_write(swap_disk, sec_no * SECTORS_IN_PAGE + i
							, page->va + DISK_SECTOR_SIZE * i);
	}
	/* 3. swap slot의 비트를 true(할당됨)로 바꿔주기 */
    //    해당 페이지의 PTE에서 present bit을 0으로 바꿔준다.
    //    이제 프로세스가 이 페이지에 접근하면 page fault가 뜬다.    
    bitmap_set(swap_table, sec_no, true);
    pml4_clear_page(thread_current()->pml4, page->va);
    /* 페이지의 swap_index 값을 이 페이지가 저장된 swap slot의 번호로 써준다.*/
    anon_page->swap_index = sec_no;
    return true;
	
}
swap out : 메인 메모리 꽉 참 -> 퇴출할 페이지 고름 -> 디스크에 복사하기 -> 메모리에 삭제
swap in : 디스크에 복사해둔 내용으로 페이지 생성 -> 스왑테이블 업데이트 -> 메모리에 다시 로드
파일 로부터 맵핑 된 페이지이다.
디스크에 있던 프로그램이 실행될 때 코드 섹션 과 데이터 섹션 섹션은 파일기반 페이지로 load 된다.
file-backed 페이지의 내용은 파일에서 가져오므로 mmap된 파일을 백업 저장소로 사용
file-backed 페이지를 evict하면 해당 페이지가 매핑된 파일에 다시 기록
vm/anon.c에서 file_backed_init 및 file_initializer 수정
bool
file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
	/* Set up the handler */
	page->operations = &file_ops;
	
	struct uninit_page *uninit = &page->uninit;
	
	struct aux_file_info *f_info = (struct aux_file_info*)uninit->aux;
	page->file.file = f_info->file;
	page->file.read_bytes = f_info->read_bytes;
	page->file.pg_cnt = f_info->pg_cnt;
	page->file.ofs = f_info->ofs;
	return true;
}
파일에서 콘텐츠를 읽어 kva 페이지에서 swap in
파일 시스템과 동기화
static bool
file_backed_swap_in (struct page *page, void *kva) {
	struct file_page *file_page UNUSED = &page->file;
	/*파일에서 콘텐츠를 읽어 kva 페이지에서 swap in
	  파일 시스템과 동기화하기*/
	struct file *file = file_page->file;
	size_t read_bytes = file_page->read_bytes;
	off_t ofs = file_page->ofs;
	int pg_cnt = file_page->pg_cnt;
	
	file_read_at(file, kva, read_bytes, ofs);
	return true;
}
내용을 다시 파일에 기록하여 swap out
- 먼저 페이지가 dirty 인지 확인
 
- 더럽지 않으면 파일의 내용을 수정할 필요가 없음
 
- 페이지를 교체한 후에는 페이지의 더티 비트 끄기
 
- 이상 없으면 커널 패닉이 발생할 수 있음
 
static bool
file_backed_swap_out (struct page *page) {
	struct file_page *file_page UNUSED = &page->file;
	uint64_t curr_pml4 = thread_current()->pml4;
	
	struct file *file = file_page->file;
	size_t read_bytes = file_page->read_bytes;
	off_t ofs = file_page->ofs;
	
	// dirty 한지 확인
	if(pml4_is_dirty(curr_pml4, page->va)){
		file_write_at(file, page->frame->kva, read_bytes, ofs);
		pml4_set_dirty(curr_pml4, page->va, 0);
	}
	// page-frame 연결 끊기
	page->frame->page = NULL;
	page->frame = NULL;
	pml4_clear_page(curr_pml4, page->va);
	return true;
}