메모리 스와핑 : 메모리 회수기법
누가 ?
: 운영체제 의해 이루어짐
언제 ?
: 유저 프로그램이 메모리 할당을 요청했을 때
무엇을 ?
: 사용되지 않는 메모리 프레임들을 디스크로 스왑 아웃!
어떻게 ?
: 운영체제가 감지하면 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;
}