当前位置: 首页 > article >正文

11.Lab Ten —— mmap

内存映射文件(Memory-Mapped File)是一种将文件内容映射到进程的虚拟地址空间的技术,使得文件的内容可以像内存一样被访问。

通过内存映射文件,可以高效地访问和操作文件内容

首先切换到mmap分支

git checkout mmap
make clean
  1. 在Makefile中添加 $U/_mmaptest,同时添加mmap和munmap系统调用的声明定义,包括kernel/syscall.h, kernel/syscall.c, user/usys.pluser/user.h.

2.在 kernel/proc.h 中定义VMA结构体及数组

VMA存储了mmap-ed的信息,当一个进程调用mmap后,kernel会将已经mmap的信息记录到进程的一个VMA数组中(一次mmap对应一个VMA),strcut proc中有一个vma_pool字段存储所有的VMA,是一个固定大小的数组,当在mmap需要存储一个VMA时,就是从vma_pool中找一个空闲的位置并将VMA放进去

MAX_VMA_POOL定义为16

VMA结构体字段含义:

  • used:表明VMA是否已经使用,用于在vma_pool中寻空闲位置

  • addr:在内存中映射的内存地址

  • length:映射的大小

  • prot:mmap参数中的权限

  • flags:mmap参数中的flags

  • offset:mmap参数中的offset,一般恒为0

  • f:要映射的文件

3.在kernel/proc.c中的allocproc函数(分配新的进程结构体)中增加对vma_pool的初始化

4.在kernel/proc.c中实现vma_pool中分配和释放VMA的逻辑vma_alloc和vma_fre,同时将函数放到defs.h中

5. 实现sys_mmap()逻辑(有关mmap的系统调用已经在最开始设置好)

调用system call传入参数,检查权限,然后从vma_pool中分配一个空的vma位置,将其填充mmap的相关信息

6.实现sys_munmap()逻辑

解析调用system call的参数,在vma_pool中找到munmap的内存地址所对应的VMA,根据VMA的信息,将修改数据回写到文件中,然后更新VMA的信息,同时如果发现当初mmap的内存全部munmap,那就释放VMA

int
munmap_impl(uint64 addr, int length)
{
  struct proc *p = myproc();

  // 在 vma pool 中找到 addr 对应的 vma
  struct VMA *vma = 0;
  int i;
  for (i = 0; i < MAX_VMA_POOL; i++) {
    vma = p->vma_pool + i;
    if (vma->used == 1 && addr >= vma->addr && (addr + length) < (vma->addr + vma->length)) {
      break;
    }
  }
  if (i > MAX_VMA_POOL) {
    return -1;
  }
  // 根据 vma 的信息,将数据回写入文件中
  uint64 begin_addr = addr;
  uint64 end_addr = addr + length;
  if (vma->flags == MAP_SHARED && vma->f->writable) {
    uint64 cur_addr = begin_addr;
    while (cur_addr < end_addr) {
      int sz = end_addr - cur_addr >= PGSIZE? PGSIZE: end_addr - cur_addr;
      begin_op();
      ilock(vma->f->ip);
      if (writei(vma->f->ip, 1, cur_addr, cur_addr - vma->addr, sz) != sz) {
        return -1;
      }
      iunlock(vma->f->ip);
      end_op();
      uvmunmap(p->pagetable, cur_addr, 1, 1);
      cur_addr += PGSIZE;
    }
  }
  // 完成回写后,更新 vma 中的信息
  //如果头部的取消映射,那么前面length地址长度的空间都无效了,所以需要+length
  if (addr == vma->addr) {  // 说明 addr 是 mmap 内存的头部
    vma->addr += length;
    vma->length -= length;
    //如果不是头部而是尾部,那其实对vma的起始位置没有影响,直接长度减减即可
  } else if (addr + length == vma->addr + vma->length) { // 说明 addr 是 mmap 的尾部
    vma->length -= length;
  }
  // 如果 mmap 的内存全部被 munmmap,那需要释放 vma 以及对 file 的引用
  if (vma->length == 0 && vma->used == 1) {
    filedup(vma->f);
    vma->used = 0;
  }
  return 0;
}


uint64 sys_munmap(void){
  uint64 addr;
  int length;
  if(argaddr(0,&addr) < 0 || argint(1,&length) < 0){
    return -1;
  }
  return (uint64) munmap_impl(addr,length);
}

7.在kernel/trap.c中的usertrap增加对page fault的处理,因为mmap只是分配了虚拟内存,并没有分配物理内存,当用户访问mmap的内存时会产生page fault,所以需要分配实际的物理内存

同时因为实现了COW,也就是mmap的内存是lazy allocation的,那么虚拟内存不一定有对应的物理内存,就需要修改kernel/vm.c中的uvmcopyuvmunmap

8.修改exit(kernel/proc.c)和fork(kernel/proc.c)

exit应当释放当前进程全部的mmap-ed的内存区域

fork应当让子进程拥有与父进程相同的mmaped

可以发现这里exit使用了munmap_impl函数,那么需要将该函数放到kernel/defs.h当中

测试


http://www.kler.cn/news/342907.html

相关文章:

  • 《自然语言处理NLP》—— 独热编码(One-Hot Encoding)
  • 华为 HCIP-Datacom H12-821 题库 (35)
  • Docker安装Nginx
  • 在python中如何判断回文串(一)?
  • 设计模式01-类图及设计原理(Java)
  • Golang | Leetcode Golang题解之第456题132模式
  • 为何一个简单的线上商城 两年销售7000多万?
  • 如何在uniAPP中编写页面
  • SFML库环境配置
  • Spring 如何加载多份配置文件
  • C++ | Leetcode C++题解之第468题验证IP地址
  • 奥普思凯 ——AI 扫描仪引领办公新潮流
  • MFC的.rc 和.rc2文件【常见问题】
  • C#使用ITextSharp生成PDF文件实例详解
  • 【C++】C++入门基础
  • leetcode链表(一)-移除链表元素
  • html中<div>标签设置宽度和高度都有哪些方法
  • <Project-8.1 pdf2tx-MM> Python Flask 用浏览器翻译PDF内容 2个翻译引擎 繁简中文结果 从P8更改
  • 【SQL】Windows MySQL 服务查询启动停止自启动(保姆级)
  • Leetcode 数组中第 k 大的元素