如何避免【Errno 12】使用子进程模块导致的Cannot allocate memory错误

Posted

技术标签:

【中文标题】如何避免【Errno 12】使用子进程模块导致的Cannot allocate memory错误【英文标题】:How to avoid [Errno 12] Cannot allocate memory errors caused by using subprocess module 【发布时间】:2013-12-05 08:22:22 【问题描述】:

完整的工作测试用例

当然,根据您在本地和远程计算机上的内存,您的数组大小会有所不同。

z1 = numpy.random.rand(300000000,2);
for i in range(1000):
  print('*******************************************\n'); 
  direct_output = subprocess.check_output('ssh blah@blah "ls /"', shell=True);
  direct_output = 'a'*1200000; 
  a2 = direct_output*10;
  print(len(direct_output));

当前用例

如果它有助于我的用例如下:

我发出数据库查询然后将结果表存储在远程机器上。然后我想通过网络传输它们并进行分析。到目前为止,我一直在 python 中执行以下操作:

#run a bunch of queries before hand with the results in remote files

....
counter = 0
mergedDataFrame = None
while NotDone:
  output = subprocess.check_output('ssh blah@blah cat /data/file%08d'%(counter))
  data = pandas.read_csv(...)
  #do lots of analysis, append, merge, numpy stuff etc...
  mergedDataFrame = pandas.merge(...)
  counter += 1

在某些时候,我在 check_output 命令中收到以下错误:[Errno 12] 无法分配内存

背景

感谢以下问题,我想我知道出了什么问题。发布了许多解决方案,我正在尝试确定哪些解决方案可以避免 [Errno 12] Cannot allocate memory 与使用 fork/clone 的子进程实现相关的错误。

Python subprocess.Popen "OSError: [Errno 12] Cannot allocate memory" 这给出了基本诊断并提出了一些解决方法,例如生成单独的脚本等...

Understanding Python fork and memory allocation errors 建议使用 rfoo 来规避 fork/clone 的子进程限制以及产生子进程和复制内存等...这似乎暗示了客户端-服务器模型

What is the simplest way to SSH using Python? ,但是由于内存限制和 fork/clone 实现,我有无法使用子进程的额外限制?解决方案建议使用 paramiko 或基于它构建的东西,其他人建议使用 subprocess(我发现这在我的情况下不起作用)。

还有其他类似的问题,但答案经常谈到文件描述符是罪魁祸首(在这种情况下他们不是),向系统添加更多 RAM(我不能这样做),升级到 x64(我已经在 x64 )。一些暗示 ENOMEM 的问题。一些答案提到尝试确定 subprocess.Popen(在我的情况下为 check_output)是否没有正确清理进程,但看起来 S. Lott 和其他人同意子进程代码本身正在正确清理。

Python memory allocation error using subprocess.Popen Python IOError cannot allocate memory although there is plenty Cannot allocate memory on Popen commands Python subprocess.Popen erroring with OSError: [Errno 12] Cannot allocate memory after period of time

我在 github https://github.com/paramiko/paramiko/search?q=Popen&type=Code 上搜索了源代码,它似乎在 proxy.py 文件中使用了 subprocess。

实际问题

这是否意味着最终 paramiko 正在使用上述 Popen 解决方案,当 python 内存占用增加并且由于克隆/分叉实现而进行重复的 Popen 调用时会出现问题?

如果 paramiko 不起作用,是否有另一种方法可以通过仅客户端解决方案来做我正在寻找的事情?还是需要客户端/服务器/套接字解决方案?如果是这样,rfoo、tornado 或 zeromq、http 传输中的任何一个都可以在这里工作吗?

注意事项 我正在运行 64 位 linux 8GB 主内存。我不想追求购买更多 RAM 的选择。

【问题讨论】:

1.我可以使用simpler example 重现该问题。 (64bit linux) 2. 通常我希望paramiko 使用套接字来创建连接。 你试过How to scp in python? 我确实看到了那个链接,谢谢。他们似乎主要建议 paramiko。 Paramiko(通过套接字)应该解决子进程模块 fork/clone 问题是否正确?我对 paramiko 的主要担忧(可能是错误的)是我进行了快速的源代码搜索,发现它使用了 subprocess 模块——尽管我不知道是为了什么,也不知道在多大程度上。 Paramiko 是一个原生的 python ssh 实现。它不为传输的任何部分使用子进程。 相关:Python subprocess.Popen “OSError: [Errno 12] Cannot allocate memory” 【参考方案1】:

如果内存不足,您可能需要增加交换内存。或者您可能根本没有启用交换。在 Ubuntu(它也应该适用于其他发行版)中,您可以通过以下方式检查您的交换:

$sudo swapon -s

如果它为空,则表示您没有启用任何交换。添加 1GB 交换:

$sudo dd if=/dev/zero of=/swapfile bs=1024 count=1024k
$sudo mkswap /swapfile
$sudo swapon /swapfile

将以下行添加到fstab 以使交换永久化。

$sudo vim /etc/fstab

     /swapfile       none    swap    sw      0       0 

来源和更多信息可以找到here。

【讨论】:

感谢您的建议。我尝试检查交换大小,它启用了 10GB 的交换空间。 这给了我一个思路,我发现我的磁盘空间已经没有可用空间了。【参考方案2】:

如果您的内存不足,可能是因为子进程试图同时读取太多内存。除了使用重定向到本地文件之外,解决方案可能是使用类似 popen 的功能和一个 stdin/stdout 对,一次可以读取一点。

【讨论】:

不,这不是因为子进程读入内存太多。这些文件只有 200-300MB。请阅读发布的链接,尤其是查看问题的第一个链接。它与子流程的实现方式有关:fork/clone。 ***.com/questions/1367373/… Linux fork/clone 是写时复制。它不应该使用大量的实际内存,除非您的交换策略不允许过度使用。 CPython 的主要缺点是 CPython 的引用计数分散在各处,这很快就会使页面变脏并需要复制。 如果您想出一个具有适当输入和预期输出的sscce.org,我将有兴趣更深入地研究这个问题。我试过了: output = subprocess.check_output(['ssh', 'localhost', 'cat', '/etc/services']) ,但它没有复制问题。 您使用的是什么操作系统?你用的是什么版本的 Python? 如果您使用的是 Linux,您的过度使用策略是什么? github.com/torvalds/linux/blob/master/Documentation/sysctl/…【参考方案3】:

应该这样做:

http://docs.python.org/3.3/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3

这样,您可以读取行或块,而不是一次读取整个内容。

【讨论】:

我不确定我是否遗漏了什么,但我不想替换 os.popen,我已经在使用子进程。问题不是实际文件大小,而是子进程中的 fork/clone

以上是关于如何避免【Errno 12】使用子进程模块导致的Cannot allocate memory错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 网站中使用子进程模块

linux下如何用c语言调用shell命令

运行 NPM 子进程导致“找不到模块”

OSError: [Errno 2] 在 Django 中使用 python 子进程时没有这样的文件或目录

OSError:[Errno 2]在Django中使用python子进程时没有这样的文件或目录

如何为python安装子进程模块?