sw사관학교정글/PintOS(KAIST's CS330 class)

[week11] PintOS - Project 3(Virtual Memory) : Swap In/Out

D cron 2022. 1. 26. 00:46

Project 3 : Swap In/Out 구현

무엇을 하는가?

Memory swapping은 physical memory 사용을 극대화하기 위한 memory 회수 기법이다. main memory의 frame이 할당되면 system은 user program의 메모리 할당 요청을 더 이상 처리할 수 없다. 한 가지 해결책은 최근에 사용되지 않는 memory frame을 disk로 교체하는 것이다. 이렇게 하면 일부 memory resource가 확보되어 다른 응용프로그램에서 사용 가능해진다.


가상 주소 안의 VPN에 Present Bit을 두어 해당 페이지가 디스크로 스왑되었는지를 나타낸다.


swapping은 OS 에 의해 실행된다. system에서 memory가 부족한데 메모리 할당 요청을 받으면 교환할(evict) page를 고른다. 그 다음 메모리 frame의 정확한 상태가 disk에 복사된다. 프로세스가 swap out된 page에 액세스하려고 하면 OS는 정확한 내용을 memory에 다시 가져와서 page를 복구한다.

구현

disk에서 free swap slot과 used swap slot을 관리하는 자료구조인 swap table을 구현하자.

// vm/anon.c
struct bitmap *swap_table;
const size_t SECTORS_PER_PAGE = PGSIZE / DISK_SECTOR_SIZE;

swap table에서 bitmap을 사용하는데, 각각의 bit는 swap slot에 대응된다. swap slot에 해당하는 bit가 1이라는 것은 page가 swap out되었다는 의미이다.

SECTORS_PER_PAGE를 보면, PGSIZE는 4kb(4096byte), DISK_SECTOR_SIZE는 512byte이므로 8개의 disk sector가 page마다 있는 것이다.


  • disk sector란 hard disk의 최소 기억 단위이다. 일반적으로 HDD의 경우 512byte의 크기를 갖는다고 한다.

vm_anon_init 수정

// vm/anon.c
void
vm_anon_init (void) {
    /* TODO: Set up the swap_disk. */
    swap_disk = disk_get(1,1);
    size_t swap_size = disk_size(swap_disk) / SECTORS_PER_PAGE;
    swap_table = bitmap_create(swap_size);
}

bitmap_create는 모든 bit들을 false로 초기화하는 함수, 사용되면 bit를 true로 바꾼다.

anon_swap_out 구현

anonymous page를 swap out한다. disk상에 swap disk 공간에 임시로 page를 저장한다.

// vm/anon.c
/* Swap out the page by writing contents to the swap disk. */
static bool
anon_swap_out (struct page *page) {
    struct anon_page *anon_page = &page->anon;
    // project 3
    // swap table에서 page를 할당받을 수 있는 swap slot 찾기
    int page_no = bitmap_scan(swap_table, 0, 1, false);
    if(page_no == BITMAP_ERROR){
        return false;
    }
    // 한 page를 disk에 쓰기 위해 SECTORS_PER_PAGE개의 섹터에 저장한다.
    // 이 때 disk의 각 섹터의 크기(DISK_SECTOR_SIZE)만큼 써 준다.
    for(int i=0; i<SECTORS_PER_PAGE; ++i){
        disk_write(swap_disk, page_no *SECTORS_PER_PAGE + i, page->va + DISK_SECTOR_SIZE * i);
    }
    // swap table의 해당 page에 대한 swap slot의 bit를 ture로 바꿔준다.
    // 해당 page의 pte에서 present bit을 0으로 바꿔준다.
    // 이제 프로세스가 이 page에 접근하면 page fault가 뜬다.
    bitmap_set(swap_table, page_no, true);
    pml4_clear_page(thread_current()->pml4, page->va);
    // page의 swap_index 값을 이 page가 저장된 swap slot의 번호로 써준다.
    anon_page->swap_index = page_no;

    return true;
}

anon_swap_in 구현

swap out된 page에 저장된 swap_index 값으로 swap slot을 찾아 해당 slot에 저장된 data를 가지고 다시 page를 memory에 복원시킨다.

// vm/anon.c
/* Swap in the page by read contents from the swap disk. */
static bool
anon_swap_in (struct page *page, void *kva) {
    struct anon_page *anon_page = &page->anon;
    // swap out된 page가 disk swap영역 어느 위치에 저장되었는지는 
    // anon_page 구조체 안에 저장되어 있다.
    int page_no = anon_page->swap_index;

    if(bitmap_test(swap_table, page_no) == false){
        return false;
    }
    // 해당 swap 영역의 data를 가상 주소공간 kva에 써준다.
    for(int i=0; i< SECTORS_PER_PAGE; ++i){
        disk_read(swap_disk, page_no * SECTORS_PER_PAGE + i, kva + DISK_SECTOR_SIZE * i);
    }
    // 해당 swap slot false로 만들어줌(다음번에 쓸 수 있게)
    bitmap_set(swap_table, page_no, false);

    return true;
}

이제 file-backed page의 swap in, swap out을 살펴보자.

file_backed_swap_in 구현

file에서 contents를 읽어 와서 swap in을 진행한다.

// vm/file.c
static bool
file_backed_swap_in (struct page *page, void *kva) {
    struct file_page *file_page UNUSED = &page->file;

    if(page == NULL){
        return false;
    }

    struct container *aux = (struct container*)page->uninit.aux;

    struct file *file = aux->file;
    off_t offset = aux->offset;
    size_t page_read_bytes = aux->page_read_bytes;
    size_t page_zero_bytes = PGSIZE - page_read_bytes;

    file_seek(file, offset);

    if(file_read(file, kva, page_read_bytes) != (int)page_read_bytes){
        return false;
    }

    memset(kva + page_read_bytes, 0, page_zero_bytes);

    return true;
}

file_backed_swap_out 구현

page가 수정되었다면 file에 수정사항을 기록하면서 swap out 시킨다. 수정사항 확인은 dirty로 확인한다.

static bool
file_backed_swap_out (struct page *page) {
    struct file_page *file_page UNUSED = &page->file;

    if(page==NULL){
        return false;
    }

    struct container *aux = (struct container *)page->uninit.aux;

    // dirty check
    if(pml4_is_dirty(thread_current()->pml4, page->va)){
        file_write_at(aux->file, page->va, aux->page_read_bytes, aux->offset);
        pml4_set_dirty(thread_current()->pml4, page->va, 0);
    }
    pml4_clear_page(thread_current()->pml4, page->va);
}

process.c 에서 process_cleanup 부분을 수정해야 한다.

/* Free the current process's resources. */
static void
process_cleanup (void) {
    struct thread *curr = thread_current ();

#ifdef VM
    if(!hash_empty(&curr->spt.pages)){
        supplemental_page_table_kill(&curr->spt);
    }
    // supplemental_page_table_kill (&curr->spt);
#endif
    ...
}