본문 바로가기
정글 2기/OS 운영체제

[pintos] Project 3_Virtual Memory(가상 메모리)_2

by Dean30 2021. 10. 27.
728x90

[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;
}

 

 

728x90

댓글