3.Lab-10 mmap

Lab: mmap (hard)

一、要求

  1. 只要求实现mmap功能的子集,即对文件进行内存映射

mmap说明

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
  1. map:成功时, 返回map的虚拟地址,失败时返回 `0xffffffffffffffff``

  2. addr:内存映射的起始地址,通常设置为 NULL,内核自行决定文件映射到的虚拟地址,本次Lab中addr始终为零

  3. len :要映射的字节数,可能与文件长度不同

  4. prot: 内存映射模式:可读、可写、或可执行

    可以假定 protPROT_READPROT_WRITE 或两者都是

  5. flags

    • MAP_SHARED:对映射内存的修改应写回文件

    • MAP_PRIVATE:修改无需写回文件

    • 不需要实现其他flags

  6. fd :要映射的文件描述符

  7. offset:文件映射的起始位置,通常为文件的偏移量,单位为字节,本次Lab始终为零

如果映射同一个 MAP_SHARED 文件的进程不共享物理页面,这是可以接受的。

munmap说明

int munmap(void *addr, size_t len);
  1. 移除指定地址范围内的 mmap 映射。

  2. 如果内存被修改,并且映射模式为 MAP_SHARED,则应先将修改写入文件。

  3. 一个 munmap 调用可能只覆盖 mmap 区域的一部分,但可以假定它将在开始处、结束处或整个区域取消映射(但不会在区域中间打一个“洞”)。

    比如map了0到10,unmap的区域可能是[0,x]、[x, 10],或者[0,10],但不会是[x,y]\((x,y \in (0,10))\)

一些提示

  1. UPROGS中添加_mmaptest,并添加mmapmunmap系统调用,以便能够编译 user/mmaptest.c。目前,只需让mmapmunmap返回错误。kernel/fcntl.h中定义了PROT_READ等。运行mmaptest,它将在第一次mmap调用时失败。

  2. 懒填充页表:mmap不应分配物理内存或读取文件。应在usertrap的页面错误处理中完成这些操作,就像copy-on-write LAB中一样。之所以要懒加载,是为了确保map 大文件时能够快速完成,并且mmap一个大于物理内存的文件也是可能的。

    • 这里的map 大文件能够快速完成:

      • 因为无需真正读取,而只是记录,所以mmap系统调用返回的快
      • 之后发生缺页中断时,因为是懒加载,所以每次只读取需要的文件内容,无需全部读取,所以速度也很快
    • mmap一个大于物理内存的文件:

      和COW一样,因为只读取需要的页,所以可mmap一个大于物理内存的文件

  3. 跟踪mmap的元数据,定义一个结构体VMA保存mmap的元数据。记录虚拟内存范围的地址、长度、权限、文件等。由于xv6没有内核内存分配器,可声明一个固定大小的元数据数组VMAS,大小为16即可。

  4. 实现mmap:在进程的地址空间中找到一个未使用的区域来映射文件,并在进程的VMA数组中添加一个VMA。VMA应包含一个指向映射文件的struct file的指针;mmap应该增加文件的引用计数,以便在关闭文件时结构体不会消失(提示:filedup)。运行mmaptest:第一次mmap应该成功,但随后访问mmap区域的内存将会出现页面错误并使mmaptest终止。

  5. 当访问map区域内存时,会发生缺页异常,

    添加代码:申请一个新页,将相关文件的4096字节读入该页,并将其映射到用户地址空间。

    使用readi读取文件,它接受文件偏移量参数,用于读取正确的位置(必须对传递给readi的inode进行lock\unlock)。正确设置页面的权限。

    运行mmaptest;它应该能够运行到第一个munmap。

  6. 实现munmap:找到地址范围对应的VMA并取消映射(提示:使用uvmunmap)。如果munmap移除了先前mmap的所有页面,它应该减少相应struct file的引用计数。如果取消映射的页面已被修改并且文件映射为MAP_SHARED,则应将页面写回文件(参考:filewrite)。

  7. 理想情况下,只需写回脏页。RISC-V PTE中的脏位(D)表示页面是否被修改。不过,mmaptest并不检查非脏页面是否没有被写回;因此,可以不查看D位,全写回页面。

  8. 修改exit,当exit时,应取消map的区域,就像调用了munmap一样。

  9. 修改fork,以确保子进程和父进程有相同的Map区域。不要忘记为VMA的struct file增加引用计数。在子进程的页面错误处理程序中,可以分配一个新的物理页面,而不是与父进程共享页面。后者会更酷,但它需要更多的实现工作。运行mmaptest;

三、代码地址

https://github.com/INnoVationv2/xv6-labs-2023/commit/4b0cf67a16ffe29e53b3774175ce32a78190156a

image-20240420234931652
posted @ 2024-04-21 00:17  INnoVation-V2  阅读(6)  评论(0编辑  收藏  举报