理解Python的fork和内存分配错误
我有一个内存使用量很大的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()之间的区别
Python subprocess.Popen在一段时间后出现OSError: [Errno 12] Cannot allocate memory的错误
1 个回答
看起来没有真正的解决方案会出现(也就是说,没有一个使用vfork的subprocess的替代实现)。那么,我们来个小花招吧?在你的程序开始时,先启动一个小助手进程,它占用的内存很少,随时准备启动你的子进程,并在主进程的整个生命周期内与它保持开放的通信。
这里有个例子,使用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的参数,但这应该都比较简单。
你还应该能很容易地在客户端开始时启动服务器,并管理套接字文件(或端口),确保在退出时进行清理。