
vm.c)struct frame의 연결 리스트struct frame에 struct list_elem 필드를 추가해야 함struct frame에 필드 추가// include/vm/vm.h
/* The representation of "frame" */
// kva : 커널 가상 메모리
struct frame {
// 이미 있는 필드
void *kva; // 커널가상 주소
struct page *page; // 페이지 구조체
// 추가할 필드
struct list_elem (...); // frame table(리스트) 삽입용도
struct *thread (...); // 프레임을 참조하는 쓰레드
};
vm.c에서 선언vm_init에서 list_init으로 초기화// vm/vm.c
#include "vm/vm.h"
struct list frame_table;
void vm_init(void) {
vm_anon_init();
vm_file_init();
#ifdef EFILESYS /* For project 4 */
pagecache_init();
#endif
register_inspect_intr();
/* DO NOT MODIFY UPPER LINES. */
/* TODO: Your code goes here. */
list_init(&frame_table);
}
vm_get_frame에서 struct frame을 만들어 줄 때마다, 프레임 테이블에 삽입해야 함.palloc_get_page로 프레임을 찾지 못했을 때 panic을 때렸는데, 이제 frame을 evict한 뒤 다시 palloc을 재시도하게끔 코드 변경.vm_evict_frame은 내쫓을 프레임을 정하고 swap out으로 스왑영역(anon) OR 파일(file-backed)에 저장. 이후 해당 프레임 반환.palloc_free_page로 해당 frame의 메모리 할당 해제.static struct frame *vm_get_frame(void) {
struct frame *frame = NULL;
// 새로운 페이지 할당 -> palloc 은 kva 반환
void * new_page = palloc_get_page(PAL_USER);
// (A) 이걸 panic에서 vm_evict_frame으로 변경하고, palloc 재시도로 변경.
if(new_page == NULL){
//vm_evict_frame...
//반환한 frame을, palloc_free_page로 메모리 할당해제...
}
// 프레임 구조체 할당
frame = calloc(1,sizeof(struct frame));
if(frame == NULL){
// 이쪽은 프레임을 찾는 함수가 아니니, 계속 panic 처리해도 될듯.
PANIC("to do\n");
}
list_push_back(&frame_table, &frame->elem); // (B) 이걸 변경
// frame 구조체 멤버 변수 초기화
// 실제 물리 메모리 page 할당
frame->page = NULL;
// 물리 메모리 주소 -> 가상 주소로 변환
frame->kva = new_page;
ASSERT(frame != NULL);
ASSERT(frame->page == NULL);
/* 실제 프레임의 Page는 매핑되기 전까지 빈 New_page를 만들기만 해두고,kva에 newpage에 대한 주소 정보를 담고있어서 나중에 매핑할 때 할당받은 newpage에 데이터를 넣는 느낌인가? */
return frame;
}
palloc이 실패할 때 단순히 에러 뜨고 핀토스 종료시키는 게 아니라, 기존 프레임 중 하나를 희생시켜야 함.bool pml4_is_accessed (uint64_t *pml4, const void *vpage);로 참조bool pml4_set_accessed (uint64_t *pml4, const void *vpage, bool accessed);로 값 변경// vm/vm.c
/* Get the struct frame, that will be evicted. */
static struct frame *vm_get_victim(void) {
struct frame *victim = NULL;
/* TODO: The policy for eviction is up to you. */
// 앞선 clock algorithm 이용해, frame table 순회하고, 희생시킬 프레임을 반환한다.
return victim;
}
vm_get_victim에서 구현./* Evict one page and return the corresponding frame.
* Return NULL on error.*/
static struct frame *vm_evict_frame(void) {
struct frame *victim UNUSED = vm_get_victim();
/* TODO: swap out the victim and return the evicted frame. */
// swap_out(victim -> page) 호출
// page와 frame 간 매핑을 page table에서 없애기
return NULL;
}
anon_swap_in)swap_out(page) 호출할 것. page에는 struct page *가 들어가는데, victim의 page 필드 확인하면 됨.victim과 가상페이지의 매핑을, 페이지 테이블에서 제거해야 함pml4는 victim에서 현재 쓰레드를 가리키는 필드 확인하고, 거기서 가져오면 됨.upage는 victim이 가리키는 page의 va...anon.c)vm_anon_init에서 디스크 구조체 static struct disk를 초기화해야 함.// vm/anon.c
static struct disk *swap_disk; // swap disk, global variable
/* Initialize the data for anonymous pages */
void vm_anon_init(void) {
/* TODO: Set up the swap_disk. */
swap_disk = NULL;
}
devices/disk.c의 disk_get으로 초기화 가능. 우린 스왑 영역을 위한 디스크가 필요하니, DEV_NO=1, CHAN_NO=1로 해주면 될듯.// devices/disk.c
/* 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) {
// 생략
}
// vm/anon.c
void vm_anon_init(void) {
swap_disk = disk_get(1, 1);
}
0: 현재 비어 있음1: 현재 사용 중lib/kernel/bitmap.c의 bitmap_create로 생성 가능/* Initializes B to be a bitmap of BIT_CNT bits
and sets all of its bits to false.
Returns true if success, false if memory allocation
failed. */
struct bitmap *bitmap_create(size_t bit_cnt){
// 생략
}
bit_cnt는 스왑 영역의 개수. 스왑 영역의 개수는 디스크 크기 / 4096바이트disk_sector_t disk_size(struct disk *d)로 확인512바이트임// include/devices/disk.h
/* Size of a disk sector in bytes. */
#define DISK_SECTOR_SIZE 512
/* Index of a disk sector within a disk.
* Good enough for disks up to 2 TB. */
typedef uint32_t disk_sector_t;
bit_cnt에는 디스크 크기 / 4096 = disk_size(...) * 512 / 4096 = disk_size(...) / 8을 넣어주면 될 듯.// vm/anon.c
static struct disk *swap_disk; // swap disk, global variable
static struct bitmap *swap_table; // 새롭게 선언
/* Initialize the data for anonymous pages */
void vm_anon_init(void) {
/* TODO: Set up the swap_disk. */
swap_disk = disk_get(1, 1);
swap_table = bitmap_create(disk_size(swap_disk) / 8);
}
anon.c)struct anon_pagestruct page 내 struct anon_page에, swap out된 페이지일 경우 몇 번째 swap slot에 위치해 있는지 저장할 필드를 만들어 두어야 함.// vm/anon.h
struct anon_page {
disk_sector_t swap_slot;
};
anon_initializer에서 별도의 값 설정을 하진 않아도 됨.-1 같은 초기값을 줘도 되긴 할듯.anon_destroy/* Destroy the anonymous page. PAGE will be freed by the caller. */
static void anon_destroy(struct page *page) {
struct anon_page *anon_page = &page->anon;
}
anon_swap_in/* 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된 anonymous page가 swap in되는 경우 anon_swap_in이 호출됨.
vm_do_claim_page의 swap_in 함수에서.anon_swap_in이 아니라 uninit_initialize가 호출됨에 유의.void disk_read(struct disk *d, disk_sector_t sec_no, void *buffer)
anon_page -> swap_slot으로 swap할 첫 sector 번호 확인sec_no: anon_page -> swap_slot * 8부터 시작해, 반복마다 1씩 증가.*8 해주는 이유? 섹터 8개 == swap_slot 1개buffer: 매개변수 kva부터 시작해, 반복마다 512씩 증가1(사용중) -> 0(비었음)으로 변경bitmap_set(struct bitmap *b, size_t idx, bool value)idx는 swap_slot./* 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;
}
void disk_write(struct disk *d, disk_sector_t sec_no, const void *buffer)
0(비었음)인 첫 비트 찾기size_t bitmap_scan(const struct bitmap *b, size_t start, size_t cnt, bool value). 값이 value인 첫 비트의 인덱스를 반환. start는 0, size_t는 1로 두면 됩니다.anon_page -> swap_slot에 해당 비트 인덱스 저장sec_no: anon_page -> swap_slot * 8부터 시작해, 반복마다 1씩 증가*8 해주는 이유? 섹터 8개 == swap_slot 1개buffer: page -> frame -> kva...하면 되지 않을려나?0(비었음) -> 1(사용중)으로 변경bitmap_set(struct bitmap *b, size_t idx, bool value)idx는 swap_slot.file.c)struct file_pageswap_in, swap_out 시 파일을 읽고 쓰는데 필요한 함수를 필드로 저장. 얘네 4개가 필요한데 이젠 익숙하실 것.// include/vm/file.h
struct file_page{
struct file *file; // 파일 주소
size_t ofs; // offset
size_t page_read_bytes; // 실제 데이터가 저장된 바이트 수
size_t page_zero_bytes; // zero padding된 바이트 수
}
file_initializer/* Initialize the file backed page */
bool file_backed_initializer(struct page *page, enum vm_type type, void *kva) {
// page -> uninit -> aux를 별도 변수로 저장.
/* Set up the handler */
page->operations = &file_ops;
struct file_page *file_page = &page->file;
// file_page의 각 필드에, 저장해둔 값 복사.
return true;
}
page는 uninitialized page임.page -> uninit -> aux를 별도의 변수에 저장file, ofs, page_read_bytes, page_zero_bytes가 있었죠file_backed_destroy/* Destory the file backed page. PAGE will be freed by the caller. */
static void file_backed_destroy(struct page *page) {
struct file_page *file_page UNUSED = &page->file;
}
file_backed_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;
}
file_read_at 함수를 이용.file_page -> file에 저장된 파일을 ofs부터 page_read_bytes만큼 읽고, page_zero_bytes만큼 memset.lazy_load_segment, mmap 구현했을 때와 비슷한 흐름으로 하면 될 듯함.file_backed_swap_out/* 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;
}
file_write_at 함수를 이용.file_page -> file의 ofs 위치부터 page_read_bytes만큼 작성.munmap 구현했을 때와 비슷한 흐름으로 하면 될 듯함.