Python中的子进程模块资源暂时不可用错误



在Python中,我生成一个gnuplot进程来从数据集生成gif图像。

from subprocess import Popen, PIPE
def gnuplotter(...)
    p = Popen([GNUPLOT], shell=False, stdin=PIPE, stdout=PIPE)
    p.stdin.write(r'set terminal gif;')
    ...
    p.stdin.write(contents)
    p.stdout.close()

它工作正常,当我使用gnuplotter()一次,但当我启动进程多次,我得到Resource temporarily unavailable错误。

for i in range(54):
    gnuplotter(i, ... 
  File "/Users/smcho/code/PycharmProjects/contextAggregator/aggregation_analyzer/aggregation_analyzer/gnuplotter.py", line 48, in gnuplotter
    p = Popen([GNUPLOT], shell=False, stdin=PIPE, stdout=PIPE)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1205, in _execute_child
    self.pid = os.fork()
OSError: [Errno 35] Resource temporarily unavailable

怎么了,我怎么能关闭gnuplot进程喷涌另一个?

pid号、打开文件描述符、内存都是有限的资源。

fork(2)手册说什么时候errno.EAGAIN应该发生:

<>之前[EAGAIN]系统对进程总数的限制执行将被超越。这个限制取决于配置。系统对进程总数的限制MAXUPRC ()将超过单个用户的执行次数。之前

为了更容易地重现错误,您可以在程序开始处添加:

import resource
resource.setrlimit(resource.RLIMIT_NPROC, (20, 20))

问题可能是所有的子进程都是活的,因为你没有调用p.stdin.close(), gnuplot的stdin可能被完全缓冲,当重定向到一个管道,即gnuplot进程可能被卡住等待输入。和/或你的应用程序使用了太多的文件描述符(Python 2.7默认情况下,文件描述符由子进程继承)而没有释放它们。

如果输入不依赖于输出,并且输入的大小有限,则使用.communicate():

from subprocess import Popen, PIPE, STDOUT
p = Popen("gnuplot", stdin=PIPE, stdout=PIPE, stderr=PIPE,
          close_fds=True, # to avoid running out of file descriptors
          bufsize=-1, # fully buffered (use zero (default) if no p.communicate())
          universal_newlines=True) # translate newlines, encode/decode text
out, err = p.communicate("n".join(['set terminal gif;', contents]))

.communicate()写所有的输入,读所有的输出(并发的,所以没有死锁),然后关闭p.stdin, p.stdout, p.stderr(即使输入很小,gnuplot的一侧是完全缓冲的;EOF(清空缓冲区)并等待进程完成(没有僵尸)。

Popen在其构造函数中调用_cleanup(),该构造函数轮询所有已知子进程的退出状态,即,即使您不调用p.wait(),也不应该有很多僵尸进程(死亡但未读状态)。

需要应该调用p.wait()等待子进程完成,然后在完成与子进程的通信后收集它。

如果你有特殊的情况(你想开始N然后等待),p.poll()会让你检查一个是否已经完成。

既然已经设置了管道,就应该使用p.communicate()来避免死锁。

最新更新