os.fork() 在 Python 中是使用写时复制还是完全复制父进程?
我想把一个比较大的数据结构加载到一个进程中,然后再进行分叉,希望能减少总的内存使用。请问在Linux(RHEL)系统中,os.fork
会这样工作吗?它会复制整个父进程吗?
2 个回答
3
如果你在一个 *nix 系统上,那么 os.fork()
会使用系统的 fork()
调用。在 Linux 系统中,这个调用是采用“写时复制”的方式:
http://linux.die.net/man/2/fork
可以查看“备注”部分
25
即使使用了COW(写时复制),CPython仍然使用引用计数,并把引用计数存储在每个对象的头部。所以,除非你对这些数据不做任何操作,否则你很快就会在内存中产生一些无效的写入,这会迫使系统复制数据。比如,你把数据传给一个函数?这就增加了一个引用,叫做INCREF
,这又是在COW内存上写入。把它存储在一个变量或者对象属性里?也是一样。就连你只是查找它的方法,情况也是如此。
一些内置的数据结构会把大部分数据单独分配,而不是和对象放在一起(例如,大多数集合),这是出于各种原因。如果这些数据分配在不同的内存页上——或者说COW工作的其他粒度上——你可能会在这些情况下运气不错。然而,从这样的集合中引用的对象并不例外——使用它同样会改变它的引用计数。
此外,某些数据会被共享,因为设计上没有对它进行写入(例如,原生的CPython代码),而你的fork
出来的进程可能没有触碰到的一些对象也可能被共享(我其实不太确定;我认为循环垃圾回收不会对对象进行写入)。但是,Python代码使用的Python对象几乎可以肯定会被写入。类似的道理也适用于PyPy、Jython、IronPython等(只是它们在对象头部处理一些位,而不是进行引用计数),不过我不能保证所有可能的配置都是这样。