# Lab mmap

切换分支:

$ git fetch
$ git checkout mmap
$ make clean

阅读:https://mit-public-courses-cn-translatio.gitbook.io/mit6-s081/lec17-virtual-memory-for-applications-frans

实现 mmap 和 munmap 并通过 mmaptest 测试程序。

  1. 首先增加系统调用使得能够正常通过编译。

  2. 理解 mmap,munmap 形参及其作用,前者是建立映射,后者是消除映射。于在进程之间共享内存,将文件映射到进程地址空间。

    void *mmap(void *addr, size_t length, int prot, int flags,
               int fd, off_t offset);
    
    int munmap(void *addr, size_t length);
    

对于 mmap

  1. 第一个参数是一个想映射到的特定地址,如果传入null表示不指定特定地址,这样的话内核会选择一个地址来完成映射,并从系统调用返回。此时假设 addr 是 0 。
  2. 第二个参数是想要映射的地址段长度 len。
  3. 第三个参数是 Protection bit ,例如读写R|W。
  4. 第四个参数暂时跳过不做讨论,它的值可以是 MAP_PRIVATE 。它指定了如果你更新了传入的对象,会发生什么。(注,第四个参数是flags,MAP_PRIVATE是其中一个值,在mmap文件的场景下,MAP_PRIVATE表明更新文件不会写入磁盘,只会更新在内存中的拷贝,详见man page)。
  5. 第五个参数是传入的对象,在上面的例子中就是文件描述符。
  6. 第六个参数是 offset。

具体作用是将文件描述符中指向的文件内容,根据起始地址加上 offset 开始,映射 len 长度,将这块区域映射到特定的内存地址上。然后只需要通过指针就可以访问,而不用通过 read/write 系统调用就可以直接从磁盘读取文件内容。这个接口可以用来操纵存储在文件中的数据结构。

mmap 和常规文件操作的区别,或者说优势?

总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此mmap效率更高。

mmap 还可以用作他途。除了可以映射文件之外,还可以用来映射匿名的内存(Anonymous Memory)。这是sbrk(注,详见8.2)的替代方案,你可以向内核申请物理内存,然后映射到特定的虚拟内存地址。

对于 munmap

   int munmap(void *addr, size_t length);

功能是消除从 addr 开始,长度为 length 地址之间的映射。

  1. 实现惰性加载(lab5)。也就是当真正需要的时候才进行映射,此处调用 vmatrylazytouch 函数。
    1. 首先获取当前进程,根据进程和传进来的虚拟地址拿到对应的 vma 。
    2. 申请一块内存用于存放从 inode 中读取的数据。
    3. 设置标志位然后建立映射。
  2. 定义 vma 结构体,在进程的结构体中定义 vma 数组,因为没有内存分配器所以先定义 16 个空槽来用。
  3. 实现 munmap , 取消从 addr 开始,长度为 sz 的内存映射。
    1. 文档中调用 uvmunmap, 重写 uvmunmap 增加脏页写回磁盘的处理。
  4. 修改 fork 父子进程共享 vma 且要增加 vma 的引用计数。