fork 后的 COW 如何工作?

How does COW after fork work?

在现代类 UNIX 系统中执行 fork 之后,我正在阅读有关使用 COW 方法的信息。

假设我们有进程 — P1。它分叉;我们得到另一个进程——P2。 由于 COW,它们的虚拟内存由相同的物理页面支持。 有一个页面,其中一个静态全局变量(例如 main 之外的 static long variable;)位于(在 .data 段中)由物理支持第 A 页

现在P1改变了它的静态全局变量;内核在处理保护故障后,将新的页面(页面B)映射到P1的虚拟内存中以存储更改的变量。

同P2修改其静态全局变量一样,内核在处理完保护错误后,映射一个新的页面(页面C)到P1的虚拟内存中来存储这个变化的变量。

现在没有任何内容引用页面 A。 它位于哪里?我想这不是 "hanging in the air" 不使用一个物理页面,从而浪费内存吗?

创建页面 B 时,页面 A 上的 COW 标志被删除,因为该页面不再共享;不再需要在修改之前复制它。因此,P2 简单地使用页面 A,可能根本不会导致页面错误,当然也不需要复制页面。因此,没有页面 C,页面 A 也没有被引用。

请注意,如果在修改页面 A 上的变量之前,P1 再次分叉,或 P2 分叉,或两者兼而有之,则可能有 3 个或更多进程引用该页面。系统通常会在内存映射控制信息中为每个页面维护一个引用计数器,记录有多少个进程将该页面映射到自己的进程内存中,这个计数控制着COW标志是否可以被清除。在只有一个进程引用该页面之前,COW 标志一直有效。

exec 操作将减少旧进程中所有页面的引用计数,如果引用计数变为零,这将释放页面以供重用。如果 P1 设置了一些显式共享内存,即使引用计数器可以大于 1,共享内存页面也不会设置 COW 标志。

页面 A 不会悬空,因为只有一个副本发生。

两个进程中的一个会先触发COW。它会得到新的帧B,而另一个进程坚持使用A。

我们可以安排其他进程不出现页面错误。这可能充满了竞争,尤其是在 SMP 下,每个内核都有自己的 TLB。

或者我们可以让其他进程也出现页面错误。它将知道帧 A 不再需要复制,因为,比方说,管理 object 中有一个跟踪 A 的引用计数,并且该引用计数的值为 1,表示 A 是唯一映射的。所以页面错误处理程序只会将页面标记为存在,并将其映射到 A.

如果 parent 生成 child,child 退出,然后 parent 触及先前共享的页面,则同样的事情也会发生。由于不再共享,因此没有理由 copy-on-write 它。