理解Python中的fork和内存分配错误

10

我有一个内存密集型的Python应用程序(大约几百MB到几GB不等)。 我还有一些非常小的Linux可执行文件,主要是为了运行该应用程序,例如:

child = Popen("make html", cwd = r'../../docs', stdout = PIPE, shell = True)
child.wait()

当我使用subprocess.Popen运行这些外部工具(在长主进程运行结束时)时,有时会出现OSError: [Errno 12] Cannot allocate memory的错误。
我不明白为什么它会发生...要求的进程很小!
系统有足够的内存可以支持更多的shell。
我使用Linux(Ubuntu 12.10,64位),因此我猜 subprocess 调用了 Fork。
而 Fork 又复制了我的现有进程,从而使消耗的内存翻倍,并失败了??
“写时复制”机制是怎么回事?
我能否在没有 fork(或者起一个新进程而不复制内存 - 重新开始)的情况下生成一个新进程?
相关: fork()、vfork()、exec()和clone()的区别 fork()和内存分配行为 Python subprocess.Popen 在一段时间后出现 OSError: [Errno 12] Cannot allocate memory错误 使用 subprocess.Popen 出现 Python 内存分配错误

1
你读过这个相关问题的答案了吗?链接 - jfs
1
我已经知道了,谢谢。那里有一些很有价值的解决方法,其中一些我可能会使用。我希望得到一个真正的解决方案 - 即在Python内部生成一个不会复制所有进程内存(像fork一样)的新进程的能力。 - Tal Weiss
1个回答

4
似乎没有真正的解决方案出现(即使用vfork实现的替代subprocess)。那么,如何进行一个巧妙的hack呢?在进程开始时,生成一个待机状态下的从属进程,其内存占用很小,准备好为您的子进程生成并在主进程的生命周期中保持与其通信。

以下是使用rfoo示例(http://code.google.com/p/rfoo/),使用名为rfoosocket的命名Unix套接字(您可以使用其他连接类型rfoo支持或其他RPC库):

服务端:

import rfoo
import subprocess

class MyHandler(rfoo.BaseHandler):
    def RPopen(self, cmd):
        c = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
        c.wait()
        return c.stdout.read()

rfoo.UnixServer(MyHandler).start('rfoosocket')

客户端:

import rfoo

# Waste a bunch of memory before spawning the child. Swap out the RPC below
# for a straight popen to show it otherwise fails. Tweak to suit your
# available system memory.
mem = [x for x in range(100000000)]

c = rfoo.UnixConnection().connect('rfoosocket')

print rfoo.Proxy(c).RPopen('ls -l')

如果您需要与生成的子进程进行实时双向协处理交互,那么这个模型可能不适用,但您可能可以通过一些技巧来使其适用。您应该根据自己的具体需求清理Popen可用参数,但这应该是相对简单的。
您还应该很容易地在客户端启动服务器,并管理套接字文件(或端口)以在退出时清理。

如果cmd产生足够的输出来填满其stdout操作系统管道缓冲区,则Ropen会导致死锁。 - jfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接