Page Fault

  • 通过 page fault 可以实现的一系列虚拟内存功能

    • lazy allocation
    • copy-on-write fork
    • demand paging
    • memory mapped files
  • 虚拟内存好处

    • isolation,隔离性;虚拟内存使得操作系统可以为每个应用程序提供属于它们自己的地址空间
    • level of indirection,提供了一层抽象,处理器和所有的指令都可以使用虚拟地址,内核会定义从虚拟地址到物理地址的映射关系
  • page fault 得到的信息

    • 引起 page fault 的内存地址(STVAL 寄存器)
    • 引起 page fault 的原因类型(SCAUSE 寄存器)
    • 引起 page fault 时的程序计数器值,这表明了 page fault 在用户空间发生的位置(SEPC 寄存器中,trapframe->epc)

sbrk

  • 当 sbrk 实际发生或者被调用的时候,内核会分配一些物理内存,并将这些内存映射到用户应用程序的地址空间,然后将内存内容初始化为 0,再返回 sbrk 系统调用。这样,应用程序可以通过多次 sbrk 系统调用来增加它所需要的内存。类似的,应用程序还可以通过给 sbrk 传入负数作为参数,来减少或者压缩它的地址空间。

lazy allocation

  • sbrk 系统调基本上不做任何事情,唯一需要做的事情就是提升p->sz,将p->sz增加 n,其中 n 是需要新分配的内存 page 数量。但是内核在这个时间点并不会分配任何物理内存。
  • 之后在某个时间点,应用程序使用到了新申请的那部分内存,这时会触发 page fault,因为我们还没有将新的内存映射到 page table。
  • 所以,如果我们解析一个大于旧的p->sz,但是又小于新的p->sz(注,也就是旧的 p->sz + n)的虚拟地址,我们希望内核能够分配一个内存 page,并且重新执行指令。

Zero Fill On Demand

  • 在一个正常的操作系统中,如果执行 exec,exec 会申请地址空间,里面会存放 text 和 data。因为 BSS 里面保存了未被初始化的全局变量,这里或许有许多许多个 page,但是所有的 page 内容都为 0。

  • 通常可以调优的地方是,我有如此多的内容全是 0 的 page,在物理内存中,我只需要分配一个 page,这个 page 的内容全是 0。然后将所有虚拟地址空间的全 0 的 page 都 map 到这一个物理 page 上。这样至少在程序启动的时候能节省大量的物理内存分配。

  • 只有 BSS 进行修改才重新分配一个新的页面

Copy-on-Write Fork

  • 创建子进程时,直接共享父进程的物理内存 page\
  • 更改内容时,触发 page fault,得到 page fault 之后,我们需要拷贝相应的物理 page
  • 需要对于每一个物理内存 page 的引用进行计数,当我们释放虚拟 page 时,我们将物理内存 page 的引用数减 1,如果引用数等于 0,那么我们就能释放物理内存 page

Memory Mapped Files

  • 个现代的操作系统会提供一个叫做 mmap 的系统调用。这个系统调用会接收一个虚拟内存地址(VA),长度(len),protection,一些标志位,一个打开文件的文件描述符,和偏移量(offset)

  • 从文件描述符对应的文件的偏移量的位置开始,映射长度为 len 的内容到虚拟内存地址 VA

  • 信息在 VMA(virtual memory area)中存储;当我们得到一个位于 VMA 地址范围的 page fault 时,内核可以从磁盘中读数据,并加载到内存中。