정글사관학교 개발일지/운영체제-PintOS

PintOS Project 3 - Virtual Memory (6) swap in/out(정글사관학교 83일차 TIL)

Woonys 2022. 1. 25. 00:08
반응형

Gitbook 정리는 요기에.

 

1. swap in/out 관련 셋업 구현

vm/anon.c에 있는 vm_anon_init과 anon_initializer를 수정해라

 

Gitbook 설명을 보면, anon page는 어떤 백업 공간도 갖고 있지 않다고 한다. 따라서 anon page를 swapping하는 것을 지원해주기 위해 swap disk라고 하는 임시 공간을 제공해준다. swap_disk는 디스크 내부에 있는 공간을 뜻한다. anon page에 대한 swap을 구현하기 위해 swap disk를 구현해보자. 

 

swap_table 구현

 

swap table은 swap disk에서 사용 가능한 영역과 사용 중인 영역을 구분하기 위한 테이블로, 비트맵 형식으로 구현한다. bitmap으로 해당 정보를 구현시 1bit면 정보를 표시할 수 있기에 간단하게 구현이 가능하다.

 

SECTORS_PER_PAGE(페이지당 섹터 수)는 스왑 영역을 페이지 사이즈 단위로 관리하기 위한 값으로 생각하면 되겠다. 아래 주석에 써놓은 것처럼 스왑 영역은 디스크로, 디스크에서는 섹터라는 단위(512 bytes)로 정보를 관리한다. 그런데 이를 페이지 단위로 관리하려면 섹터 단위를 페이지 단위로 바꿀 필요가 있다. 이를 해주는 게 SECTORS_PER_PAGE. 즉, 8섹터 당 1 페이지를 뜻한다.

@/vm/anon.c

/* 
스왑 디스크에서 사용 가능한 영역과 사용된 영역을 관리하기 위한 자료구조로 bitmap 사용
스왑 영역은 PGSIZE 단위로 관리 => 기본적으로 스왑 영역은 디스크이니 섹터로 관리하는데
이를 페이지 단위로 관리하려면 섹터 단위를 페이지 단위로 바꿔줄 필요가 있음.
이 단위가 SECTORS_PER_PAGE! (8섹터 당 1페이지 관리)
*/
struct bitmap *swap_table;
int bitcnt;
const size_t SECTORS_PER_PAGE = PGSIZE / DISK_SECTOR_SIZE;

 

vm_anon_init() 구현

 

먼저 vm_anon_init()부터 구현하자. anonymous page를 위해 디스크 내 스왑 공간을 생성해주는 함수이다. 미리 제공해주는 함수인 disk_get()을 사용한다. disk_get()은 해당 인자에 맞는 디스크를 반환해주는 함수이다. 인자로 들어가는 숫자는 disk number(DEV_NO)와 channel number(CHAN_NO)라고 하는데, 주석을 보면 그냥 각 번호에 정보가 매핑되어 있다고 보면 될듯. 인자로 1, 1이 들어가면 swap에 해당한다. 따라서 우리는 swap disk를 반환받는다. (0,0)이면 부팅 로더가 들어있는 디스크, (0, 1)이면 파일 시스템이 들어있는 디스크 이런 식인듯 하다.

 

이어서 반환받은 디스크의 사이즈(disk_size(swap_disk))를 SECTORS_PER_PAGE로 나눠준다. 이 SECTORS_PER_PAGE는 PGSIZE(4096 bytes)를 DISK_SECTOR_SIZE(512 bytes)로 나눠준 값(=8)이다. 디스크 섹터는 하드 디스크 내 정보를 저장하는 단위로, 자체적으로 주소를 갖는 storage의 단위다. 즉, 한 페이지 당 몇 개의 섹터가 들어가는지를 나눈 값을 swap_size로 지칭한다. 즉, 해당 swap_disk를 swap할 때 필요한 섹터 수가 결국 swap_size.

 

마지막으로 주어진 swap size 크기만큼 swap_table을 비트맵으로 생성한다. 이 스왑 테이블은 할당받은 스왑 디스크를 관리하는 테이블로, 주어진 함수 bitmap_create()를 이용한다.

 

disk_get()

더보기

디스크 공간을 return받는 함수인듯.

/* Returns the disk numbered DEV_NO--either 0 or 1 for master or
   slave, respectively--within the channel numbered CHAN_NO.

   Pintos uses disks this way:
0:0 - boot loader, command line args, and operating system kernel
0:1 - file system
1:0 - scratch
1:1 - swap
*/

 

@/vm/anon.c

(...)
static struct disk *swap_disk;

/* Initialize the data for anonymous pages */
void
vm_anon_init (void) {
	/* TODO: Set up the swap_disk. */
	//struct anon_page anonymous_page;
	swap_disk = disk_get(1, 1);
    size_t swap_size = disk_size(swap_disk) / SECTORS_PER_PAGE;
    swap_table = bitmap_create(swap_size);
}

 

anon_initializer() 수정

 

anon_initializer()는 파일을 처음으로 가상 페이지에 load할 때 load_segment() -> vm_alloc_page_with_initializer()에서 실행된다.

/* Initialize the file mapping */
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;
}

 

2. anonymous page 관련 swap in/out 구현

anon_swap_out() 구현

 

anon_swap_out()은 anonymous page를 디스크 내 swap 공간으로 내리는 작업을 수행하는 함수이다.

 

비트맵을 순회해 false 값을 갖는(=해당 swap slot이 비어있다는 표시) 비트를 찾는다. 이어서 해당 섹터에 페이지 크기만큼 써줘야 하니 필요한 섹터 수 만큼 disk_write()을 통해 입력해준다. write 작업이 끝나면 해당 스왑 공간에 페이지가 채워졌으니 bitmap_set()으로 slot이 찼다고 표시해준다. 그리고 pml4_clear_page()로 물리 프레임에 올라와 있던 페이지를 지운다.

/* 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;

    /* 비트맵을 처음부터 순회해 false 값을 갖는 비트를 하나 찾는다.
    즉, 페이지를 할당받을 수 있는 swap slot을 하나 찾는다. */
	int page_no = bitmap_scan(swap_table, 0, 1, false);

    if (page_no == BITMAP_ERROR) {
        return false;
    }

    /* 
    한 페이지를 디스크에 써주기 위해 SECTORS_PER_PAGE 개의 섹터에 저장해야 한다.
    이때 디스크에 각 섹터 크기의 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의 해당 페이지에 대한 swap slot의 비트를 true로 바꿔주고
    해당 페이지의 PTE에서 present bit을 0으로 바꿔준다.
    이제 프로세스가 이 페이지에 접근하면 page fault가 뜬다.
    */
    bitmap_set(swap_table, page_no, true);
    pml4_clear_page(thread_current()->pml4, page->va);

    /* 페이지의 swap_index 값을 이 페이지가 저장된 swap slot의 번호로 써준다.*/
    anon_page->swap_index = page_no;

    return true;
}

 

anon_swap_in() 구현

 

위에서 swap out했던 anon page를 다시 메모리로 불러들인다.

/* 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; // 해당 페이지를 anon_page로 변경

	int page_no = anon_page->swap_index; // anon_page에 들어있는 swap_index


    if (bitmap_test(swap_table, page_no) == false) {
        return false;
    }

    for (int i = 0; i < SECTORS_PER_PAGE; ++i) {
        disk_read(swap_disk, page_no * SECTORS_PER_PAGE + i, kva + DISK_SECTOR_SIZE * i);
    }

    bitmap_set(swap_table, page_no, false);
    
    return true;
}

 

 

3. file-backed page 관련 swap in/out 구현

 

이번에는 file-backed page 관련 swap in/out을 구현한다. 

 

file_backed_swap_out() 구현

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 page)인지 체크
    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);
}

 

anon_swap_in() 구현

/* Swap in the page by read contents from the file. */
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) {
        // palloc_free_page (kva);
        return false;
    }

	memset (kva + page_read_bytes, 0, page_zero_bytes);

    return true;
}
반응형