이번에는 프로젝트 3의 마지막 구현과제인 Swap In/Out에 대해서 구현을 해보도록 하겠습니다.
먼저 메모리 스왑은 물리적 메모리 사용량을 최대화하기 위한 메모리 회수 기술입니다. 주 메모리의 프레임이 할당되면 시스템은 사용자 프로그램의 메모리 할당 요청을 더 이상 처리할 수 없습니다. 한 가지 해결책은 현재 사용되지 않는 메모리 프레임을 디스크에 스왑아웃하는 것입니다. 이렇게 하면 일부 메모리 리소스를 확보하여 다른 애플리케이션에서 사용할 수 있습니다.
Anonymous Page에는 백업 스토리지가 없기 때문에 스왑을 지원하기 위해 스왑 디스크라는 임시 백업 스토리지를 만들어서 사용합니다. 그에 반해 File Mapped Page는 콘텐츠를 파일에서 가져오기 때문에 매핑된 파일을 백업 저장소로 사용해야 합니다. 즉, 파일 백업 페이지를 내보내면 매핑된 파일에 다시 기록됩니다.
구현
1️⃣ void vm_anon_init (void)
- 스왑 디스크를 설정하고, 또한 스왑 디스크에서 사용 가능한 영역과 사용 중인 영역을 관리하기 위한 데이터 구조가 필요합니다.
- 스왑 영역은 PGSIZE(4096바이트) 단위로 관리됩니다.
- disk sector_size가 512바이트로 주어졌기 때문에 PGSIZE로 맞추기 위해 8개씩 반복을 하며 하나의 슬롯으로 관리합니다.
- 스왑 공간을 사용하고있는지 아닌지를 판단하는 bool 형태의 use를 사용합니다.
void
vm_anon_init (void) {
/* TODO: Set up the swap_disk. */
list_init(&swap_list);
swap_disk = disk_get(1, 1);
disk_sector_t max_sector_size = disk_size(swap_disk);
for(int i =0; i< max_sector_size; i += 8){
struct swap_anon* swap_anon = malloc(sizeof(struct swap_anon));
swap_anon->page=NULL;
swap_anon->use = false;
for(int j = 0; j<8; j++)
swap_anon->sectors[j] = i+j;
list_push_back(&swap_list, &swap_anon->swap_elem);
}
}
2️⃣ bool anon_swap_out (struct page *page)
- 메모리에서 디스크로 내용을 복사하여 익명 페이지를 스왑 디스크로 교체합니다.
- 먼저 스왑 테이블을 사용하여 디스크에서 사용 가능한 스왑 슬롯을 찾은 다음 데이터 페이지를 해당 슬롯에 복사합니다.
- 데이터의 위치는 페이지 구조에 저장되어야 합니다.
- 디스크에 더 이상 여유 슬롯이 없으면 커널을 패닉 시킬 수 있습니다.
static bool
anon_swap_out (struct page *page) {
struct anon_page *anon_page = &page->anon;
struct swap_anon *swap_anon = find_blank_swap();
for(int i=0; i<8 ; i++){
disk_write(swap_disk,swap_anon->sectors[i], page->frame->kva + DISK_SECTOR_SIZE * i);
}
swap_anon->use = true;
anon_page->swap_anon = swap_anon;
page->frame = NULL;
pml4_clear_page(page->curr->pml4, page->va);
return true;
}
swap_anon의 sector를 8개씩 반복문을 돌면서 page->frame->kva 주소부터 sector_size만큼 더해가면서 disk_write를 진행합니다. 그리고 swap_anon에 스왑을 했으니 use를 true로 해주고 page->frame에 있던 값들을 정리해 줍니다.
이런 과정을 통해 anon_swap_out() 함수는 주어진 페이지를 스왑 디스크로 이동시키고, 페이지와 관련된 데이터를 업데이트하여 페이지를 메모리에서 제거하는 역할을 수행합니다.
3️⃣ bool anon_swap_in (struct page *page, void *kva)
- 디스크에서 메모리로 데이터 내용을 읽어 스왑 디스크에서 익명 페이지로 스왑 합니다.
- 데이터의 위치는 페이지가 교체될 때 스왑 디스크에 저장되어 있어야 하는 페이지 구조체입니다.
- 스왑 테이블을 업데이트합니다.
static bool
anon_swap_in (struct page *page, void *kva) {
struct anon_page *anon_page = &page->anon;
struct swap_anon *swap_anon = anon_page->swap_anon;
for(int i=0; i<8 ; i++){
disk_read(swap_disk,swap_anon->sectors[i],kva + DISK_SECTOR_SIZE * i);
}
anon_page->swap_anon = NULL;
swap_anon->use = false;
return true;
}
swap_in은 disk_write을 통해 swap_anon sector에 적어놨던 메모리를 불러와서 페이지의 프레임에 쓰는 역할을 합니다. 그러고 나서 swap_anon을 사용하지 않는다는 의미의 use = false로 설정해 줍니다. 따라서 anon_swap_in() 함수는 스왑 디스크에서 페이지 데이터를 읽어와 주어진 가상 주소에 메모리에 로드하고, 페이지와 관련된 데이터를 업데이트하여 스왑 인 작업을 수행합니다.
4️⃣ static bool file_backed_swap_out (struct page *page)
- 파일에 내용을 다시 써서 페이지를 바꿉니다.
- 먼저 페이지가 더럽지 않은지 확인하는 것이 좋습니다.(pml4_is_dirty)
- 페이지가 더럽지 않다면 파일의 내용을 수정할 필요가 없습니다.
- 페이지를 교체한 후에는 해당 페이지의 더티 비트를 0으로 해줘야 합니다.
static bool
file_backed_swap_out (struct page *page) {
struct file_page *file_page UNUSED = &page->file;
struct file* file = file_page->file; // 파일 포인터 갱신
if(file && pml4_is_dirty(thread_current()->pml4, page->va)){
file_write_at(file, page->frame->kva, file_page->read_bytes, file_page->ofs);
pml4_set_dirty(thread_current()->pml4,page->va,0);
}
page->frame = NULL;
page->swap =true;
pml4_clear_page(thread_current()->pml4, page->va);
return true;
}
5️⃣ static bool file_backed_swap_in (struct page *page, void *kva)
- 파일에서 내용을 읽어와서 kva에서 페이지를 교체합니다.
- 파일 시스템과 동기화해야 합니다.
static bool
file_backed_swap_in (struct page *page, void *kva) {
struct file_page *file_page UNUSED = &page->file;
struct file* file = file_page->file;
off_t ofs = file_page->ofs;
int page_read_bytes = file_page->read_bytes;
int page_zero_bytes = file_page->zero_bytes;
file_seek(file,ofs);
page_read_bytes == (int)file_read(file,page->frame->kva, page_read_bytes);
memset (page->frame->kva+page_read_bytes, 0, page_zero_bytes);
page->swap =false;
return true;
}
위를 끝으로 project3의 모든 구현이 끝났습니다. 중간중간 예외 처리 및 코드 수정이 있었는데 너무 많은 곳에서 일어나서 하나하나 수정 안된 부분이 있을 수 있습니다. 아래의 github - dbscks97 branch 코드를 통해 최종 코드를 확인할 수 있습니다.
https://github.com/Blue-club/pintos2_Team3/tree/dbscks97
아래 discussions에서는 진행된 과정과 트러블 슈팅에 관해서 기록해 놨습니다. 참고하실 분들은 확인하셔도 좋을 것 같습니다.
https://github.com/Blue-club/pintos2_Team3/discussions
'운영체제' 카테고리의 다른 글
DeadLock과 Redis 대기열 사용하기 - (2) (1) | 2023.08.31 |
---|---|
DeadLock과 Redis 대기열 사용하기 - (1) (0) | 2023.08.30 |
[Pintos-Kaist] Project3 - Stack Growth, Memory Mapped Files (0) | 2023.06.24 |
[Pintos-Kaist] Project3 - Anonymous Page (0) | 2023.06.19 |
[Pintos-Kaist] Project3 - Memory Management (0) | 2023.06.16 |