是否fork()立即在Linux中复制整个进程堆?


30

一个fork()系统调用克隆会从正在运行的进程子进程。除了PID以外,这两个过程是相同的。

自然地,如果进程只是从其堆中读取而不是对其进行写入,则复制堆将浪费大量内存。

是否复制了整个进程堆?是否以仅写入触发堆复制的方式进行了优化?

Answers:


19

整体fork()使用mmap实现/写时复制。

这不仅影响堆,还影响共享库,堆栈,BSS区域。

顺便说一句,这意味着fork是一个非常轻量级的操作,直到结果2个进程(父进程和子进程)实际开始写入内存范围为止。此功能是造成fork-bomb杀伤力的主要因素-在内核因页面复制和差异而超载之前,最终会导致太多进程。

您将很难在现代OS中找到内核执行硬拷贝(设备驱动程序除外)的操作示例-使用VM功能远非如此,容易得多且效率更高。

甚至execve()本质上是“请在二进制文件上映射ld.so / whatnot,然后执行”-虚拟机将处理实际加载到RAM的进程并执行。局部未初始化变量最终从“零页”映射-特殊的只读只读复制页包含零,局部初始化变量最终从二进制文件本身被映射(再次写时复制),等等


Java进程是一个值得注意的例外。搜索“ fork java内存”,您会发现许多影响大型服务器JVM嵌入式JVM的问题,这些问题试图执行小的shell命令并在“无法分配内存”异常中崩溃(这些只是随机链接,此问题是系统性的Java环境)。这个SO答案指责JVM的垃圾回收器和JIT编译器保持进程内存不被共享。
WhiteWinterWolf

24

fork()调用时,Linux内核确实实现了写时复制。执行系统调用时,父级和子级共享的页面被标记为只读。

如果在只读页面上执行写操作,则将其复制,因为两个进程之间的内存不再相同。因此,如果仅执行读取操作,则将根本不会复制页面。


1
+1谢谢!1.您能否提供参考链接?2.堆是全部复制还是部分复制?
亚当·马坦

4
2.-在页面中:)内核对“堆”是什么几乎一无所知-对于内核,这只是一堆mmapped专用页面,libc分配器可以根据需要对其进行处理。
qdot 2014年

这真的一定是叉子炸弹吗?在我看来,这段代码不会创建当前进程,而是创建同一程序的更多实例,这些实例从开始而不是从fork()调用后的下一条指令开始执行。
sherrellbc 2014年

@mmk仅供参考,我对您的“有趣的旁注:”感到非常惊讶,因此我进行了测试(在Linux 3.2.0上),但事实并非如此。我曾经/proc/self/pagemap为测试目的确定虚拟地址到物理页面的映射。如我所料,如果孙子和只有孙子写共享页面,则父级和原始子级将继续共享它。只有孙子才能得到私人副本。
Celada 2014年

@Celada。嗯 我曾在某处阅读过此书,但我不记得它所指的内核版本(可能是较旧的版本?),因此,它可能不再有效。
毫米

10

Linux执行写时复制。由于fork创建一个新的进程,分配的网页被标记为只读和父母和孩子之间共享。当它们中的任何一个试图修改页面时,都会产生页面错误,从而导致复制页面并适当地调整页面表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.