[pintos] Project 3_Virtual Memory(가상 메모리)_2
Memory mapped files
mmap and munmap System Call
void *mmap (void *addr, size_t length, int writable, int fd, off_t offset);
// systcall.c
void
syscall_handler (struct intr_frame *f UNUSED) {
// TODO: Your implementation goes here.
switch (f->R.rax)
...
case SYS_MMAP:
f->R.rax = (uint64_t) mmap ((void*) f->R.rdi, (size_t) f->R.rsi, (int) f->R.rdx, (int) f->R.r10, (off_t) f->R.r8);
break;
...
}
static void*
mmap (void *addr, size_t length, int writable, int fd, off_t offset){
//Handle all parameter error and pass it to do_mmap
if (addr == 0 || (!is_user_vaddr(addr))) return NULL;
if ((uint64_t)addr % PGSIZE != 0) return NULL;
if (offset % PGSIZE != 0) return NULL;
if ((uint64_t)addr + length == 0) return NULL;
if (!is_user_vaddr((uint64_t)addr + length)) return NULL;
for (uint64_t i = (uint64_t) addr; i < (uint64_t) addr + length; i += PGSIZE){
if (spt_find_page (&thread_current() -> spt, (void*) i)!=NULL) return NULL;
}
struct thread_file* tf = process_get_file (fd);
if (tf == NULL) return NULL;
if (tf->std == 0 || tf->std == 1) return NULL;
if (length == 0) return NULL;
struct file* file = tf->file;
return do_mmap(addr, length, writable, file, offset);
}
#include "vm/file.h"
static void* mmap (void *addr, size_t length, int writable, int fd, off_t offset);
// thread.h
struct thread_file {
struct file* file;
int fd;
struct list_elem elem;
//for dup2, default -1
int dup_tag;
int dup_cnt;
//for stdin and stdout
int std;
};
// file.c
void *
do_mmap (void *addr, size_t length, int writable,
struct file *file, off_t offset) {
//assume all parameter errors are handled in syscall.c
off_t ofs;
uint64_t read_bytes;
for (uint64_t i = 0; i < length; i += PGSIZE){
struct mmap_info* mi = malloc (sizeof (struct mmap_info));
ofs = offset + i;
read_bytes = length - i >= PGSIZE ? PGSIZE : length -i;
mi->file = file_reopen (file);
mi->offset = ofs;
mi->read_bytes = read_bytes;
vm_alloc_page_with_initializer (VM_FILE, (void*) ((uint64_t) addr + i), writable, lazy_load_file, (void*) mi);
}
struct mmap_file_info* mfi = malloc (sizeof (struct mmap_file_info));
mfi->start = (uint64_t) addr;
mfi->end = (uint64_t) pg_round_down((uint64_t) addr + length -1);
list_push_back(&mmap_file_list, &mfi->elem);
return addr;
}
#include "threads/vaddr.h"
//Use to lazy load mmap
static bool
lazy_load_file (struct page* page, void* aux){
struct mmap_info* mi = (struct mmap_info*) aux;
file_seek (mi->file, mi->offset);
page -> file.size = file_read (mi->file, page->va, mi->read_bytes);
page -> file.ofs = mi->offset;
if (page->file.size != PGSIZE){
memset (page->va + page->file.size, 0, PGSIZE - page->file.size);
}
pml4_set_dirty (thread_current()->pml4, page->va, false);
free(mi);
return true;
}
void munmap (void *addr);
// systcall.c
void
syscall_handler (struct intr_frame *f UNUSED) {
// TODO: Your implementation goes here.
switch (f->R.rax)
...
case SYS_MUNMAP:
munmap ((void*) f->R.rdi);
break;
...
}
static void
munmap (void* addr){
do_munmap(addr);
}
static void munmap (void* addr);
// file.c
void
do_munmap (void *addr) {
//traverse mmap_file_list and find appropriate set, and destroy all
if (list_empty (&mmap_file_list)) return;
for (struct list_elem* i = list_front (&mmap_file_list); i != list_end (&mmap_file_list); i = list_next (i))
{
struct mmap_file_info* mfi = list_entry (i, struct mmap_file_info, elem);
if (mfi -> start == (uint64_t) addr){
for (uint64_t j = (uint64_t)addr; j<= mfi -> end; j += PGSIZE){
struct page* page = spt_find_page(&thread_current() -> spt, (void*) j);
spt_remove_page(&thread_current()->spt, page);
}
list_remove(&mfi->elem);
free(mfi);
return;
}
}
}
void vm_file_init (void);
// file.c
void
vm_file_init (void) {
list_init(&mmap_file_list);
}
38 -> 29 (9개 증가)
FAIL -> PASS list
- tests/vm/mmap-ro
- tests/vm/mmap-suffle
- tests/vm/mmap-misalign
- tests/vm/mmap-null
- tests/vm/mmap-over-code
- tests/vm/mmap-over-data
- tests/vm/mmap-over-stk
- tests/vm/mmap-bad-off
- tests/vm/mmap-kernel
bool file_backed_initializer (struct page *page, enum vm_type type, void *kva);
// file.c
/* Initialize the file backed page */
bool
file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
/* Set up the handler */
struct file* file = ((struct mmap_info*)page ->uninit.aux)->file;
page->operations = &file_ops;
struct file_page *file_page = &page->file;
file_page -> file = file;
return true;
}
static void file_backed_destroy (struct page *page);
// file.c
/* Destory the file backed page. PAGE will be freed by the caller. */
static void
file_backed_destroy (struct page *page) {
// TODO: On mmap_exit sometimes empty file content
struct file_page *file_page = &page->file;
//if dirty, write back to file
if (pml4_is_dirty (thread_current() -> pml4, page -> va)){
file_seek (file_page->file, file_page->ofs);
file_write (file_page->file, page->va, file_page->size);
}
file_close (file_page->file);
if (page->frame != NULL) {
list_remove (&page->frame->frame_elem);
free (page->frame);
}
}
29 -> 19 (10개 증가)
FAIL -> PASS list (10)
- tests/vm/mmap-read
- tests/vm/mmap-close
- tests/vm/mmap-unmap
- tests/vm/mmap-twice
- tests/vm/mmap-write
- tests/vm/mmap-exit
- tests/vm/mmap-clean
- tests/vm/mmap-inherit
- tests/vm/mmap-remove
- tests/vm/lazy-file
PASS -> FAIL list (1)
- tests/vm/page-parallel
Swap in/out
uninit_initializer
-> anon_initializer
-> file_backed_initializer
load_segement
-> vm_alloc_page_with_initializer(VM_ANON, ..., lazy_load_segment, ...)
mmap
-> vm_alloc_page_with_initializer(VM_FILE, ..., lazy_load_file, ...)
Anonymous page
void vm_anon_init (void);
- 변경 x
bool anon_initializer (struct page *page, enum vm_type type, void *kva);
// anon.c
/* Initialize the file mapping */
bool
anon_initializer (struct page *page, enum vm_type type, void *kva) {
/* Set up the handler */
page->operations = &anon_ops;
if(type & VM_MARKER_0) page->operations = &anon_ops;
struct anon_page *anon_page = &page->anon;
anon_page->owner = thread_current();
anon_page->swap_slot_idx = INVALID_SLOT_IDX;
return true;
}
// anon.h
struct anon_page {
struct thread* owner;
size_t swap_slot_idx;
};
#define INVALID_SLOT_IDX SIZE_MAX
static bool anon_swap_in (struct page *page, void *kva);
// 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;
if(anon_page->swap_slot_idx == INVALID_SLOT_IDX) return false;
disk_sector_t sec_no;
// Read page from disk with sector size chunk
for (int i = 0; i < SECTORS_PER_PAGE; i++){
// convert swap slot index to reading sector number
sec_no = (disk_sector_t)(anon_page->swap_slot_idx * SECTORS_PER_PAGE) + 1;
off_t ofs = i * DISK_SECTOR_SIZE;
disk_read (swap_disk, sec_no, kva+ofs);
}
// Clear swap table
bitmap_set (swap_table, anon_page->swap_slot_idx, false);
anon_page->swap_slot_idx = INVALID_SLOT_IDX;
return true;
}
static bool anon_swap_out (struct page *page);
// 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;
// Get swap slot index from swap table
size_t swap_slot_idx = bitmap_scan_and_flip (swap_table,0,1,false);
if (swap_slot_idx = BITMAP_ERROR)
PANIC("There is no free swap slot!");
// Copy page frame content to swap_slot
if (page == NULL || page->frame == NULL || page->frame->kva == NULL)
return false;
disk_sector_t sec_no;
// Write page to disk with sector size chunk
for (int i = 0; i < SECTORS_PER_PAGE; i++){
// Convert swap slot index to writing sector number
sec_no = (disk_sector_t)(swap_slot_idx * SECTORS_PER_PAGE) + i;
off_t ofs = i * DISK_SECTOR_SIZE;
disk_write (swap_disk, sec_no, page->frame->kva + ofs);
}
anon_page->swap_slot_idx = swap_slot_idx;
// Set "not present" to page, and clear.
pml4_clear_page (anon_page->owner->pml4, page->va);
pml4_set_dirty (anon_page->owner->pml4, page->va, false);
page->frame = NULL;
return true;
}
File-Mapped Page
static bool file_backed_swap_in (struct page *page, void *kva);
// file.c
/* 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 (file_page->file == NULL) return false;
file_seek (file_page->file, file_page->ofs);
off_t read_size = file_read (file_page->file, kva, file_page->size);
if (read_size != file_page->size) return false;
if (read_size < PGSIZE)
memset (kva + read_size, 0, PGSIZE - read_size);
return true;
}
/* Initialize the file backed page */
bool
file_backed_initializer (struct page *page, enum vm_type type, void *kva) {
/* Set up the handler */
struct file* file = ((struct mmap_info*)page ->uninit.aux)->file;
page->operations = &file_ops;
struct file_page *file_page = &page->file;
file_page -> file = file;
return true;
}
#include <string.h>
#include "threads/malloc.h"
static bool file_backed_swap_out (struct page *page);
// file.c
/* 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;
struct thread *curr = thread_current ();
if (pml4_is_dirty (curr->pml4, page->va)) {
file_seek (file_page->file, file_page->ofs);
file_write (file_page->file, page->va, file_page->size);
pml4_set_dirty (curr->pml4, page->va, false);
}
// Set "not present" to page, and clear.
pml4_clear_page (curr->pml4, page->va);
page->frame = NULL;
return true;
}
기타
read와 write에 buffer 타당한지 check
// syscall.c
void
syscall_handler (struct intr_frame *f UNUSED) {
// TODO: Your implementation goes here.
struct thread* curr = thread_current ();
curr->saved_sp = f->rsp;
switch (f->R.rax)
{
...
case SYS_READ:
check_valid_buffer(f->R.rsi, f->R.rdx, f->rsp, 1);
f->R.rax = read(f->R.rdi, f->R.rsi, f->R.rdx);
break;
case SYS_WRITE:
check_valid_buffer(f->R.rsi, f->R.rdx, f->rsp, 0);
f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
break;
...
}
//! ADD: check_address
struct page * check_address2(void *addr){
if (is_kernel_vaddr(addr))
{
exit(-1);
}
return spt_find_page(&thread_current()->spt, addr);
}
// //! ADD: check_valid_buffer
void check_valid_buffer(void* buffer, unsigned size, void* rsp, bool to_write)
{
/* 인자로받은buffer부터buffer + size까지의크기가한페이지의크기를넘을수도있음*/
/*check_address를이용해서주소의유저영역여부를검사함과동시에vm_entry구조체를얻음*/
/* 해당주소에대한vm_entry존재여부와vm_entry의writable멤버가true인지검사*/
/* 위내용을buffer부터buffer + size까지의주소에포함되는vm_entry들에대해적용*/
for (int i = 0; i < size; i++)
{
struct page* page = check_address2(buffer + i);
if(page == NULL)
exit(-1);
if(to_write == true && page->writable == false)
exit(-1);
}
}
exit(-1) 추가
// exception.c
static void
page_fault (struct intr_frame *f) {
...
#ifdef VM
/* For project 3 and later. */
if (vm_try_handle_fault (f, fault_addr, user, write, not_present))
return;
#endif
/* Count page faults. */
page_fault_cnt++;
exit(-1); // 이게 제일 중요 !!
/* If the fault is true fault, show info and exit. */
// printf ("Page fault at %p: %s error %s page in %s context.\n",
// fault_addr,
// not_present ? "not present" : "rights violation",
// write ? "writing" : "reading",
// user ? "user" : "kernel");
/* child-bad test가 rsp의 위피를 바꾸는데 page fault는 projcet 3에서 다루므로*/
/* 일단 exit을 통해 pass를 시킨다 */
kill (f);
}
syscall의 create, open 함수 lock 이용
// syscall.c
bool create(const char *file, unsigned initial_size)
{
check_address (file);
lock_acquire (&filesys_lock);
bool result = filesys_create(file, initial_size);
lock_release (&filesys_lock);
return result;
}
int open (const char *file)
{
// file이 존재하는지 항상 체크
check_address(file);
lock_acquire (&filesys_lock);
struct file *file_obj = filesys_open(file);
lock_release (&filesys_lock);
if (file_obj == NULL)
return -1;
int fd = process_add_file(file_obj);
if (fd == -1)
file_close(file_obj);
return fd;
}
'정글 2기 > OS 운영체제' 카테고리의 다른 글
[Pintos] Project 4_File Allocating Table(FAT) vs multi-level index (0) | 2021.10.31 |
---|---|
[pintos] Project 3_Virtual Memory(가상 메모리)_실행순서 (0) | 2021.10.28 |
[pintos] Project 3_Virtual Memory(가상 메모리) (0) | 2021.10.19 |
[three_easy_pieces] 주소 공간의 개념 요약 (0) | 2021.10.15 |
[Pintos] Project 1, 2_Argument parsing, User Program_system call_code analysis (0) | 2021.10.14 |
댓글