Memory swaping은 물리 메모리의 사용을 극대화하기 위한 메모리 회수 기술이다. 메인 메모리의 프레임들이 할당될 때, 시스템은 유저 프로그램으로부터 메모리 할당 요청을 처리할 수 없다. 이를 위한 해결 방법 중 하나는 현재 디스크에 사용되지 않는 메모리 프레임을 swap out하는 것이다. 이것은 몇몇 메모리 자원들을 가용가능하게 하고 다른 어플리케이션을 사용가능하게 한다.
스와핑은 OS에 의해 실행된다. 시스템은 메모리 할당 요청을 받았는데 메모리가 부족하다면, 디스크를 스왑할 페이지를 선택한다. 그 후 메모리 프레임의 상태는 디스크에 복사된다. 하나의 프로세스가 스왑 아웃 되었던 페이지에 대한 접근을 시도할 때, OS는 정확한 내용을 메모리로 다시 가져와 페이지를 복구한다.
이 때 선택되는 페이지는 anonymous page거나 file_backed page이다. 이번 섹션에서는 페이지 타입에 따라 swap을 처리하는 것을 구현해야 한다.
모든 스와핑 작업은 명시적으로 호출되는 것이 아니라 함수의 포인터로서 호출된다. 이들은 structure page_operations file_ops의 멤버로, 각 페이지의 이니셜라이저에 대한 연산으로 등록된다.
먼저 anoymous page일 때 swap하는 것에 대해 보자.
anoymous page는 백업하는 저장소가 없다. 이 타입의 페이지를 스와핑하기 위해 swap disk라는 임시 백업 스토리지를 제공한다. 즉 스왑 디스크를 사용하여 anon_page에 대한 스왑을 구현해야 한다. vm_anon_init()함수에서 스왑디스크를 설정하는 것을 구현하라고 한다.
스왑 디스크에서 사용 가능한 영역과 사용된 영역을 관리하기 위한 자료구조가 필요하고, 스왑 영역은 PGSIZE(4096 bytes) 단위로 관리된다. 이를 위한 변수를 anon.c 파일 상단에 추가
// ../vm/anon.c
struct bitmap *swap_table;
const size_t SECTORS_PER_PAGE = PGSIZE / DISK_SECTOR_SIZE;
여기서 잠깐. bitmap이 뭐야? 일단 비트맵 구조체가 뭔지 알기 위해 참조를 타고 들어갔다.
/* Element type.
This must be an unsigned integer type at least as wide as int.
Each bit represents one bit in the bitmap.
If bit 0 in an element represents bit K in the bitmap,
then bit 1 in the element represents bit K+1 in the bitmap,
and so on. */
typedef unsigned long elem_type;
/* Number of bits in an element. */
#define ELEM_BITS (sizeof (elem_type) * CHAR_BIT)
/* From the outside, a bitmap is an array of bits. From the
inside, it's an array of elem_type (defined above) that
simulates an array of bits. */
struct bitmap {
size_t bit_cnt; /* Number of bits. */
elem_type *bits; /* Elements that represent bits. */
};
'element type인 bit는 최소한 int만큼의 너비가 있는 부호가 없는 정수 형식이어야 한다. 각각의 비트는 비트맵에서 1비트를 나타낸다. 엘레멘트에서 비트가 0인 것은 비트맵에서 비트 k를 나타내는 것이고, 요소에서 비트가 1이면 비트맵에서 비트 k+1을 나타낸다.'
'외부에서 비트맵은 비트들의 배열이다. 내부에서는 비트들의 배열을 시뮬레이션? 하는 elem_type의 배열이다.'
흠... 아리송해... 구글링 해보니 스탠포드 대학에서 pintos에 대한 설명서가 있는데, 거기에서는 비트맵 자료구조를 다음과 같이 설명한다.
Pintos includes a bitmap data structure in lib/kernel/bitmap.c and lib/kernel/bitmap.h. A bitmap is an array of bits, each of which can be true or false. Bitmaps are typically used to track usage in a set of (identical) resources: if resource n is in use, then bit n of the bitmap is true. Pintos bitmaps are fixed in size, although you could extend their implementation to support resizing.
위에서 언급한 것처럼 '스왑 디스크에서 사용 가능한 영역과 사용된 영역을 관리하기 위한 자료구조'로 비트맵이 사용된다는 것이고 비트가 0이면 해당 페이지를 사용 가능한 영역으로 선정, 1이면 참조 비트를 0으로 재설정하고, 이 때 변경 내용을 항상 디스크에 저장한다.
그래서 그 다음,
const size_t SECTORS_PER_PAGE = PGSIZE / DISK_SECTOR_SIZE;
이건 뭔데;;; 페이지당 섹터...? 뭔진 잘 모르겠지만 하나의 디스크 섹터의 사이즈는 512 bytes이고, 페이지의 오프셋은 12비트, 2^12(4096 bytes)이다. 즉 4096/512 라는 말인데.. 이게 뭘 의미하지? 저 계산대로면 페이지당 섹터는 8 bytes... 이렇게 하는거 맞아..? 누가 알려줘여...
섹터는 뭐지? 섹터(sector)는 하드 드라이브의 최소 기억 단위라고 한다. 각 세터는 고정된 양의, 사용자 접근이 가능한 데이터를 저장하며 전통적으로 HDD의 경우 512 bytes라고 한다....
아ㄱ알ㅇㄹㅇㅇㅇㅇㅇㅇㅇㅇ!!!!!!!!! 모르겠다!!!!!!!!! 일단 어떻게 활용되는지 vm_anon_init() 함수를 보면서 다시 생각해보자.
/* Initialize the data for anonymous pages */
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);
}
산 넘어 산..... disk_get() 넌 또 무엇이냐...
/* 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
*/
struct disk *
disk_get (int chan_no, int dev_no) {
ASSERT (dev_no == 0 || dev_no == 1);
if (chan_no < (int) CHANNEL_CNT) {
struct disk *d = &channels[chan_no].devices[dev_no];
if (d->is_ata)
return d;
}
return NULL;
}
...... disk 구조체는 또 어떻게 생겼는데...
/* An ATA device. */
struct disk {
char name[8]; /* Name, e.g. "hd0:1". */
struct channel *channel; /* Channel disk is on. */
int dev_no; /* Device 0 or 1 for master or slave. */
bool is_ata; /* 1=This device is an ATA disk. */
disk_sector_t capacity; /* Capacity in sectors (if is_ata). */
long long read_cnt; /* Number of sectors read. */
long long write_cnt; /* Number of sectors written. */
};
선생님 진도가 너무 빨라여... 파도파도 끝이 없어여ㅠㅠㅠㅠ ATA device는 뭐에여.... 구글링 해보니까 지금 범위를 엄청 벗어난 것 같은 기분이 드는데... 아직 OS 파일 시스템 부분도 공부하지 못했고...
여하튼 지금 이해해야 하는 것은 vm_anon_init() 에서 swap_disk를 설정하기 위해서 disk_get(1, 1)을 호출했다는 정도.. 저 부분도 나중에 다시 파 보자.
다음은 anonymous page 스와핑을 하기 위해 anon_swap_in() 과 anon_swap_out()을 구현해야 한다.
/* 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;
int page_no = 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;
}
/* 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;
int page_no = bitmap_scan(swap_table, 0, 1, false);
if (page_no == BITMAP_ERROR) {
return false;
}
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);
}
bitmap_set(swap_table, page_no, true);
pml4_clear_page(thread_current()->pml4, page->va);
anon_page->swap_index = page_no;
return true;
}
다음은 file-backed page!
이 타입의 페이지는 파일로부터 불러오기 때문에 매핑된 파일은 백업 저장소로 사용되어야 한다. 즉, 이 페이지를 제거하면 해당 페이지가 매핑된 파일에 다시 쓰여진다(write).
/* 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;
}
/* Swap out the page by writeback contents to the file. */
static bool file_backed_swap_out (struct page *page) {
struct file_page *file_page UNUSED = &page->file;
if (page == NULL)
return false;
struct box * aux = (struct box *) 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->ofs);
pml4_set_dirty (thread_current()->pml4, page->va, 0);
}
pml4_clear_page(thread_current()->pml4, page->va);
}
'개발자 도전기 > [OS] pintOS' 카테고리의 다른 글
pintOS | Project 3 회고 (0) | 2021.10.28 |
---|---|
pintOS | Project 3 | 버그잡자 (1) | 2021.10.22 |
pintOS | Project 3 | Memory Mapped Files (0) | 2021.10.20 |
pintOS | Project 3 | Stack Growth (0) | 2021.10.20 |
pintOS | Project 3 | Anonymouse Page (0) | 2021.10.19 |
댓글