分叉进程后会发生什么?

时间:2012-04-18 20:04:45

标签: c linux gcc fork x86-64

假设某个进程是从另一个进程分叉的。换句话说,我们通过fork函数调用复制一个进程。现在,因为forking是一种写时复制机制,所以当分叉进程或原始进程写入页面时,它们会得到一个新的物理页面来编写。所以我所理解的是,当分叉和原始进程都在执行时,情况就是这样。

- >在分叉时,原始和分叉进程的所有页面都被赋予只读访问权限,以便内核知道写入哪个页面。当发生这种情况时,内核将新的物理页面映射到写入过程,将先前的内容写入其中,然后提供对该页面的写入访问。现在我不清楚的是,如果fork和原始进程都写入同一页面,其中一个仍将保留原始物理页面(在分叉之前)或两者都将获得新的物理页面。其次,我的假设是正确的,分叉和原始过程中的所有页面在分叉时都被赋予只读访问权限吗?

- >现在,由于每个页面错误都会触发一个中断,这意味着每次写入原始进程或分叉进程都会降低执行速度。如果我们知道应用程序,并且我们知道将会写入很多连续的内存页面,那么当组中的一个页面之一给予多个页面(一组页面可以说)的写入权限不是更好是写的。这将减少由于页面错误处理而导致的中断次数。不是吗?当然,在这种情况下,我们有时可能会不必要地制作副本,但我认为中断比写入 long 类型的512个变量(页面的4096个字节)要多得多。我的理解是正确还是我错过了什么?

3 个答案:

答案 0 :(得分:1)

Fork在语义上创建了一个进程的副本。写时复制是一种优化,使其更快。优化通常会有一些隐藏的权衡。有些案件更快,但其他案件受到影响。写入时复制有成本,但我们希望通常会有保存,因为大多数复制的页面实际上不会由孩子写入。在理想的情况下,孩子立即执行。

因此,我们会遇到少量页面的页面错误异常,这比预先复制所有页面要便宜。

大多数“懒惰评估”类型优化具有这种性质。

完全实例化的百万项的懒惰列表比一百万项的常规列表更昂贵。但如果列表的消费者只访问前100个项目,则懒惰列表会获胜。

答案 1 :(得分:1)

如果我没有弄错的话,其中一个进程将被视为首先写入页面。即使您拥有多个内核,我相信页面错误也会被串行处理。在这种情况下,要捕获的第一个将解除两个进程的页面,所以到第二个进程写入时,不会出现错误,因为它现在将有一个可写的页面自己的。

我相信完成后,现有页面将由一个进程保留(并重新设置为读/写),并为另一个进程创建一个新副本。

我认为你的第三点围绕着一个简单的观点:“如果我们了解应用程序......”。这就是问题所在:操作系统了解应用程序。通过内核编码人员的观察,基本上它“知道”的唯一东西是间接的。毫无疑问,他们会发现fork通常后跟exec,因此他们无疑会优化这种情况。是的,情况并非总是如此,而且你显然对其他情况感到担忧 - 我在这里所说的只是他们非常不寻常,我猜他们会花费很少的精力。

我不太确定我是否遵循4096字节页面中512长度的逻辑或数学 - 写入页面的第一时间,它在进程之间被复制和解耦。从那时起,进一步写入任一进程的页面副本不会导致任何进一步的页面错误(至少与写入时的副本有关 - 当然,如果进程站点长时间闲置数据可能被分页到页面文件,或该订单上的某些内容,但这里无关紧要。)

答案 2 :(得分:0)

如果fork()不使用COW,初始成本会非常高。如果你看一下典型的top显示,RSS / VSIZE的比例非常小(例如典型的vi会话为2MB / 56MB)。

克隆没有COW的进程会导致巨大的内存压力,这实际上会导致其他进程丢失其附加页面(必须将其移动到辅助存储,以后可能会恢复)。并且该分页实际上会导致每页1-2个磁盘I / O(仅当页面是新的或脏的时,才需要 swap out swap in 只有在页面被其他进程再次引用时才需要>

另一点是粒度:在MMU不存在的日子里,必须换掉整个过程以产生内存,导致系统实际冻结第二个左右。基于每页的页面错误会导致更多的陷阱,但这些陷阱很好地分散,允许进程实际竞争物理ram 。 没有先验知识,很难击败LRU计划。