3.Lab-10 mmap
Lab: mmap (hard)
一、要求
- 只要求实现mmap功能的子集,即对文件进行内存映射
mmap说明
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
-
map
:成功时, 返回map的虚拟地址,失败时返回 `0xffffffffffffffff`` -
addr
:内存映射的起始地址,通常设置为NULL
,内核自行决定文件映射到的虚拟地址,本次Lab中addr始终为零 -
len
:要映射的字节数,可能与文件长度不同 -
prot
: 内存映射模式:可读、可写、或可执行可以假定
prot
是PROT_READ
或PROT_WRITE
或两者都是 -
flags
:-
MAP_SHARED
:对映射内存的修改应写回文件 -
MAP_PRIVATE
:修改无需写回文件 -
不需要实现其他
flags
-
-
fd
:要映射的文件描述符 -
offset
:文件映射的起始位置,通常为文件的偏移量,单位为字节,本次Lab始终为零
如果映射同一个 MAP_SHARED
文件的进程不共享物理页面,这是可以接受的。
munmap说明
int munmap(void *addr, size_t len);
-
移除指定地址范围内的
mmap
映射。 -
如果内存被修改,并且映射模式为
MAP_SHARED
,则应先将修改写入文件。 -
一个
munmap
调用可能只覆盖mmap
区域的一部分,但可以假定它将在开始处、结束处或整个区域取消映射(但不会在区域中间打一个“洞”)。比如map了0到10,unmap的区域可能是[0,x]、[x, 10],或者[0,10],但不会是[x,y]\((x,y \in (0,10))\)
一些提示
-
在
UPROGS
中添加_mmaptest
,并添加mmap
和munmap
系统调用,以便能够编译user/mmaptest.c
。目前,只需让mmap
和munmap
返回错误。kernel/fcntl.h
中定义了PROT_READ
等。运行mmaptest
,它将在第一次mmap
调用时失败。 -
懒填充页表:mmap不应分配物理内存或读取文件。应在usertrap的页面错误处理中完成这些操作,就像copy-on-write LAB中一样。之所以要懒加载,是为了确保map 大文件时能够快速完成,并且mmap一个大于物理内存的文件也是可能的。
-
这里的map 大文件能够快速完成:
- 因为无需真正读取,而只是记录,所以mmap系统调用返回的快
- 之后发生缺页中断时,因为是懒加载,所以每次只读取需要的文件内容,无需全部读取,所以速度也很快
-
mmap一个大于物理内存的文件:
和COW一样,因为只读取需要的页,所以可mmap一个大于物理内存的文件
-
-
跟踪mmap的元数据,定义一个结构体VMA保存mmap的元数据。记录虚拟内存范围的地址、长度、权限、文件等。由于xv6没有内核内存分配器,可声明一个固定大小的元数据数组VMAS,大小为16即可。
-
实现mmap:在进程的地址空间中找到一个未使用的区域来映射文件,并在进程的VMA数组中添加一个VMA。VMA应包含一个指向映射文件的struct file的指针;mmap应该增加文件的引用计数,以便在关闭文件时结构体不会消失(提示:filedup)。运行mmaptest:第一次mmap应该成功,但随后访问mmap区域的内存将会出现页面错误并使mmaptest终止。
-
当访问map区域内存时,会发生缺页异常,
添加代码:申请一个新页,将相关文件的4096字节读入该页,并将其映射到用户地址空间。
使用readi读取文件,它接受文件偏移量参数,用于读取正确的位置(必须对传递给readi的inode进行lock\unlock)。正确设置页面的权限。
运行mmaptest;它应该能够运行到第一个munmap。
-
实现munmap
:找到地址范围对应的VMA并取消映射(提示:使用uvmunmap)。如果munmap移除了先前mmap的所有页面,它应该减少相应struct file
的引用计数。如果取消映射的页面已被修改并且文件映射为MAP_SHARED,则应将页面写回文件(参考:filewrite)。 -
理想情况下,只需写回脏页。RISC-V PTE中的
脏位(D)
表示页面是否被修改。不过,mmaptest并不检查非脏页面是否没有被写回;因此,可以不查看D位,全写回页面。 -
修改
exit
,当exit时,应取消map的区域,就像调用了munmap一样。 -
修改
fork
,以确保子进程和父进程有相同的Map区域。不要忘记为VMA的struct file增加引用计数。在子进程的页面错误处理程序中,可以分配一个新的物理页面,而不是与父进程共享页面。后者会更酷,但它需要更多的实现工作。运行mmaptest;
三、代码地址
https://github.com/INnoVationv2/xv6-labs-2023/commit/4b0cf67a16ffe29e53b3774175ce32a78190156a
![image-20240420234931652](http://pic-save-fury.oss-cn-shanghai.aliyuncs.com/uPic/image-20240420234931652.png)