FreeBSD Kernel-VM System

yyi
yyi
2023-10-06 / 0 评论 / 110 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年10月06日,已超过286天没有更新,若内容或图片失效,请留言反馈。

FreeBSD Kernel-VM System

1. VM Objects

每个用户进程都看到一个单独的、私有的、连续的VM地址空间。该空间包含了不同种类的内存对象。

程序的代码段和数据段实际上是一个内存映射的文件,代码段只读,数据段写时复制(copy-on-write), BSS段按需分配,并且填充 0 (demand zero page fill)。任意文件也可以被映射到地址空间,如共享库。

对于一个写时复制的基本页,当它被加载到虚拟内存空间中,只初始化内存映射,并在之后直接返回程序的二进制段。允许VM系统可以释放或者重用这个页面,并在晚些时候把它再加载回来。当进程修改这些数据时,VM系统必须为进程提供一份私有的复制。因为这个私有复制已经被修改过了,VM系统可能不会释放它,因为以后无法恢复。

当发生fork系统调用时,复杂性会进一步增加,两个进程都有自己的私有地址空间。显然,在fork发生时制作完整的内存副本是非常不优雅的,FreeBSD通过分层的VM对象模型管理这些内容。原始的程序文件是最低的VM对象层。当进程启动后,VM系统创建一个对象层A

img

A表示可以从物理介质调入调出的文件页面。从磁盘调入是正常操作,但是我们往往不想覆写可执行程序,因此VM系统创建一个由Swap提供物理支持的对象层B

img

当第一次发生写操作时,在B中创建一个新的页面,该页面的内容从A初始化而来,该页面可以调度到swap设备上。

当程序 fork 时,VM系统创建两个新的对象层:C1和C2,其中C1是父进程、C2是子进程

img

在此情况下,当B中的一个页面被父进程修改,则该进程触发一个COW错误,并在C1中复制这个页面,并把原始的页面留存在B中。当子进程修改同一个页面时,也会触发一个COW错误,并在C2中复制这个页面。此时原始的页面在B中已经完全隐去了,当B不代表真实文件时,可能会被销毁,然而FreeBSD没有做这种优化。

假设此时,子进程执行了exec(),它的当前地址空间会被代表新文件的新地址空间替换,此时C2被破坏。

img

此时B只有一个子层,并且对B的访问全部都会通过C1,因此B和C1会折叠在一起,同时存在与B和C1的页面会在这种过程中被删除。

这种模型带来了一些问题,第一个是VM对象栈很深,会导致触发错误的时候扫描时间。第二个问题是可以能会在对象栈深处遇到死页面、不可访问的页面。(凭我理解,死页应该是永远不会被访问到的页面,比如当进程A执行完毕,进程B永远不会访问到数据A')。

FreeBSD 提供了一种名为 All Shadowed Case 的特殊优化来解决这种过深层栈导致的问题。如果C1或C2导致的COW错误已经足以完全隐藏B中的层,以C1为例,我们就可以让C1跳过B,变为C1-> A和C2->B->A,此时B只有一个子层引用(C2),B和C2可以被折叠到一起,表现得结果就是B被完全删除。

0

评论 (0)

取消