在本次实验中我们要求实现 mmap 和 munmap 系统调用,在实现之前,我们首先需要了解一下 mmap 系统调用是做什么的。根据 mmap 的描述,mmap 是用来将文件或设备内容映射到内存的。mmap 使用懒加载方法,因为需要读取的文件内容大小很可能要比可使用的物理内存要大,当用户访问页面会造成页错误,此时会产生异常,此时程序跳转到内核态由内核态为错误的页面读入文件并返回用户态继续执行。当文件不再需要的时候需要调用 munmap 解除映射,如果存在对应的标志位的话,还需要进行文件写回操作。mmap 可以由用户态直接访问文件或者设备的内容而不需要内核态与用户态进行拷贝数据,极大提高了 IO 的性能。

首先我们切换分支到 mmap

<aside> ⚠️ 本实验难度较大,需要综合应用已学知识而且花费时间很长!!!

</aside>

10.1 实现 mmap 内存映射系统调用

真正的 mmap 为用户程序提供了精细化操作它们地址空间的手段,包括但不限于:共享进程间的地址空间,将文件映射到地址空间,用户处理缺页错误等功能。我们需要为 xv6 添加的 mmap 只需要实现将文件映射到地址空间的功能,其接受的参数如下:

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

此外,另有一个用于取消 mmap 映射的系统调用 munmap(addr, length) ,除了移除映射外,其根据实际情况可能需要将映射的文件写回。

xv6 实验和往常一样,为我们提供了用户态测评程序 mmaptest 。在该测评程序中,addr和 offset 总是为 0 ,意味着内核可以自由选择映射的地址,并总是从文件开头开始映射;若映射失败,则返回 0xffffffffffffffff 。我们只需实现 mmaptest 中用到的功能。

要点

Makefile 修改和系统调用定义

首先照例在 Makefile 中添加用户态测评程序 mmaptest ,然后添加空的系统调用 mmap 和 munmap ,连接使得编译通过。

定义 vm_area 结构体及其数组

此时执行 make ,应当能够编译通过。按照 xv6 实验手册的提示,在 proc.h 中加入 VMA 数据结构的定义,并给进程控制块中加入 VMA 指针:

......
// Virtual Memory Area - lab10
struct vm_area {
    uint64 addr;    // mmap address
    int len;    // mmap memory length
    int prot;   // permission
    int flags;  // the mmap flags
    int offset; // the file offset
    struct file* f;     // pointer to the mapped file
};
......
#define NVMA 16     // the number of VMA in a process - lab10

// Per-process state
struct proc {
  // ...
  struct inode *cwd;           // Current directory
  char name[16];               // Process name (debugging)
  struct vm_area vma[NVMA];    // VMA array - lab10
};
......