MIT6.S081学习总结-lab10:mmap

lab10 实现mmap

介绍

mmap和munmap系统调用允许UNIX程序对它们的地址空间进行详细的控制。它们可以用于在进程之间共享内存,将文件映射到进程地址空间,以及作为用户级页面错误方案的一部分,比如在讲座中讨论的垃圾收集算法。在本实验中,您将向xv6添加mmap和munmap,重点关注内存映射文件。

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

mmap可以通过多种方式调用,但是这个实验只需要与内存映射文件相关的特性的子集。可以假定addr将始终为0,这意味着内核应该决定映射文件的虚拟地址,mmap返回该地址,如果失败则返回0xffffffffffffff。长度是要映射的字节数;它可能与文件的长度不相同。prot指示内存是否应该被映射为可读、可写和/或可执行的;可以假设prot是PROT_READPROT_WRITE,或者两者都是。标志可以是MAP_SHARED,这意味着对映射内存的修改应该写回文件,也可以是MAP_PRIVATE,这意味着不应该写回文件。你不需要在标志中实现任何其他位。fd是要映射的文件的打开文件描述符。可以假定offset为零(它是要映射的文件中的起始点)。如果映射同一个MAP_SHARED文件的进程不共享物理页,也没有关系。

munmap (addr, length)应该删除指定地址范围内的mmap映射。如果进程修改了内存,并将其映射为MAP_SHARED,则应该首先将修改写入文件。一个munmap调用可能只覆盖一个已映射区域的一部分,但是你可以假设它将在开始、结束或整个区域(但不会在区域中间打一个洞)取消映射。

实现

这次的hint给的已经是足够详细了。

  • 首先将_mmaptest添加到UPROGS,并调用mmap和munmap系统,以便编译user/mmaptest.c。现在,只返回来自mmap和munmap的错误。我们在kernel/fcntl.h中为你定义了PROT_READ等。运行mmaptest,它将在第一次调用mmap时失败。

  • 延迟填充页表,以响应页错误。也就是说,mmap不应该分配物理内存或读取文件。相反,应该在usertrap中的(或由usertrap调用的)页面错误处理代码中这样做,就像在lazy page allocation实验室中那样。使用lazy的原因是为了确保大文件的mmap速度快,并且mmap可以用于大于物理内存的文件。

  • 跟踪mmap为每个进程映射了什么。定义一个对应于VMA(虚拟内存区域)的结构,在Lecture 15中描述,记录地址,长度,权限,文件等由mmap创建的虚拟内存范围。因为xv6内核在内核中没有内存分配器,所以可以声明一个固定大小的vma数组,并根据需要从该数组进行分配。大小为16就足够了。

  • 实现mmap:在进程的地址空间中找到一个未使用的区域来映射文件,并在映射区域的进程表中添加一个VMA。VMA应该包含一个指向被映射文件的结构文件的指针;mmap应该增加文件的引用计数,这样当文件关闭时结构不会消失(提示:参见filedup)。运行maptest:第一个mmap应该成功,但是第一次访问被映射的内存将导致页面错误并杀死mmaptest。

  • 添加代码在映射区域中导致页错误,以便分配物理内存页,将相关文件的4096字节读入该页,并将其映射到用户地址空间。使用readi读取文件,它接受一个偏移量参数,在该参数处读取文件(但您必须锁定/解锁传递给readi的inode)。不要忘记正确设置页面上的权限。mmaptest运行;它应该会到达第一个munmap。

  • 实现munmap:查找地址范围的VMA,并解除指定页面的映射(提示:使用uvmunmap)。如果munmap删除了前一个mmap的所有页面,它应该减少相应结构文件的引用计数。如果修改了未映射的页面,并且将文件映射为MAP_SHARED,则将该页面写回该文件。从filewrite中寻找灵感。

  • 理想情况下,您的实现只写回程序实际修改的MAP_SHARED页。RISC-V PTE中的脏位(D)表示是否写入了一个页面。然而,mmaptest并不检查非脏页是否被写回;因此你可以不用看D位就可以把页面写回去。

  • 修改exit以取消进程映射区域的映射,就像调用了munmap一样。mmaptest运行;Mmap_test应该通过,但可能无法通过fork_test。

  • 修改fork以确保子节点具有与父节点相同的映射区域。不要忘记增加VMA的结构文件的引用计数。在子节点的页错误处理程序中,可以分配一个新的物理页,而不是与父节点共享一个页。后者更酷一些,但需要更多的实现工作。mmaptest运行;它应该同时通过mmap_test和fork_test。

直接在proc.h里添加VMA结构体
在这里插入图片描述
在这里插入图片描述
proc结构体里添加当前进程的VMA列表
在这里插入图片描述

添加mmap系统调用,映射的区域直接在堆上,增加p->sz即可。
在这里插入图片描述
usertrap里添加缺页中断
在这里插入图片描述

添加munmap系统调用,其中filewriteoff函数就是增加了自定义文件偏移的filewrite。
在这里插入图片描述

exec函数同样也要释放mmap
在这里插入图片描述

fork里子进程继承父进程的mmap
在这里插入图片描述
不要忘了uvmcopy和uvmunmap函数的修改,与lazy实验一样
在这里插入图片描述在这里插入图片描述

mmap in real world

内核为每个进程维护了一个单独的任务结构(task_struct),该结构中包含了内核运行该进程所需要的所有信息(例如:PID,用户栈指针,可执行目标文件名,程序计数器)。其中有一个条目指向mm_struct,描述了虚拟存储器的当前状态,,其中有两个字段为pgd和mmap,pgd指向第一级页表的基址(进程切换时会将它加载进特定寄存器,完成页表切换),mmap指向vm_area_struct的链表,相当于我们在这实现的vma。里面也包含了虚拟地址区域的起止位置,读写标志位,共享的还是私有的等。

mmap文件映射为私有时,与fork类似,使用cow的技术,当多个进程映射相同文件为私有时,先映射到同一片物理内存标记只读,当需要修改时再发生缺页中断,复制到另一片物理内存标志可写。

总结

mmap实验也是虚拟内存的应用,刚开始觉得很难,不知道从何下手,因为要考虑的地方很多,比如映射的地址区域、如何组织管理VMA、映射的长度不为PGSIZE的倍数、父子进程需不需要共享物理内存等等,后来跟着lab的hint一步一步来,发现并没有那么复杂,很多都不需要考虑,然后测试也比较弱。
经过这几次的lab,发现操作系统其实比较‘lazy’,很多需要较长时间的操作都是能拖多久是多久,例如延迟分配物理内存、cow。 毫无疑问,这种策略带来了性能上的显著提升,特别是程序启动时间,同时也最大限度减少了内存的分配。也让我感受到了虚拟内存的powerful和cool,虚拟内存yyds。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 2

打赏作者

NullObjectError

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值