PintOS

PintOS Project 3 : Virtual Memory (4) - mmap & munmap

wook's note 2024. 10. 1. 23:19

이번 포스팅은 user 가 mmap & munmap 시스템 콜을 호출할 때 상황을 보려고 한다.

프로젝트 2 시스템 콜과 같이 시스템 콜 핸들러에 mmap 과 munmap을 추가하고 do_mmap, do_munmap으로 운영체제가 유저의 요청을 처리한다.

 

mmap과 munmap은 디스크로 부터 파일 내용을 메모리에 적재할 때 사용하며 파일을 건드리는 부분에서는 lock이 필요하다.

 

 

 

 

시스템 콜 

 

system call handler{
#ifdef VM
	case SYS_MMAP:
		f->R.rax = mmap_syscall(f->R.rdi,f->R.rsi,f->R.rdx,f->R.r10,f->R.r8);
		break;
	case SYS_MUNMAP:
		munmap_syscall(f->R.rdi);
		break;
#endif
}





void *mmap_syscall(void *addr, size_t length, int writable, int fd, off_t offset){
	struct supplemental_page_table spt = thread_current()->spt;
    if (!addr || pg_round_down(addr) != addr || is_kernel_vaddr(addr) || is_kernel_vaddr(addr + length))
        return NULL;

    if (offset != pg_round_down(offset) || offset % PGSIZE != 0)
        return NULL;

    if (spt_find_page(&thread_current()->spt, addr))
        return NULL;

    struct file *file = get_file_from_fd(fd);

    if ((file >= STDIN && file <= STDERR) || file == NULL)
        return NULL;

    if (file_length(file) == 0 || (long)length <= 0)
        return NULL;

    return do_mmap(addr, length, writable, file, offset);
}

void munmap_syscall(void *addr){
	do_munmap(addr);

	return;
}

 

 

 

 

void * do_mmap (void *addr, size_t length, int writable, struct file *file, off_t offset) {
    lock_acquire(&filesys_lock);
	struct file *copied_file = file_reopen(file);
	void *start_addr = addr;

    size_t read_bytes = (length > file_length(copied_file)) ? file_length(copied_file) : length;
    size_t zero_bytes = PGSIZE - read_bytes % PGSIZE;

	// int total_page_count = length <= PGSIZE ? 1: length % PGSIZE ? length /PGSIZE + 1 : length /PGSIZE;
	ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
    ASSERT(pg_ofs(addr) == 0);      // upage가 페이지 정렬되어 있는지 확인
    ASSERT(offset % PGSIZE == 0); // ofs가 페이지 정렬되어 있는지 확인


    struct lazy_load_arg *lazy_load_arg;
	while(read_bytes > 0 || zero_bytes > 0){
		size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
        size_t page_zero_bytes = PGSIZE - page_read_bytes;

		lazy_load_arg = (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
		if (lazy_load_arg == NULL)
			goto err;
        lazy_load_arg->file = copied_file;
        lazy_load_arg->offset = offset;
        lazy_load_arg->length = page_read_bytes;


		if (!vm_alloc_page_with_initializer(VM_FILE, addr,writable, lazy_load_segment, lazy_load_arg))
			goto err;
		

		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		addr += PGSIZE;
		offset += page_read_bytes;
	}
	lock_release(&filesys_lock);
	return start_addr;

err:
    free(lazy_load_arg);
    lock_release(&filesys_lock);
    return NULL;
}

 

load_segment와 같이 파일에 대한 정보들을 uninit page에 저장해두고 #PF에 의해 lazy load된다.

 

 

void do_munmap (void *addr) {
    struct thread *curr = thread_current();
    struct page *page;

    lock_acquire(&filesys_lock);
    while ((page = spt_find_page(&curr->spt, addr))) {
        if (page)
            destroy(page);

        addr += PGSIZE;
    }
    lock_release(&filesys_lock);
}