【问题标题】:Python subprocess references result in fd exhaustionPython子进程引用导致fd耗尽
【发布时间】:2014-05-20 08:28:46
【问题描述】:

关于这个帖子:Python del Statement,

我最近遇到了以下sn-p:

# custom_process.py

import threading
import subprocess

myList = []  # module-wide list

class Foo(threading.Thread):

    myprocess = None     
    returncode = None

    def run(self):
        self.myprocess = subprocess.Popen(...)

        global myList
        myList.append(self.myprocess)

        ...  # Code skipped for brevity

        self.returncode = self.myprocess.returncode
        tmp1, tmp2 = self.myprocess.communicate()

        ...  # Code skipped for brevity

        del self.myprocess

此代码在连续调用Foorun 方法时,将用尽系统上的可用文件描述符并抛出异常:@987654325 @。

因此我想知道...在处理子进程对象时,文件描述符是否与实际的 OS 进程一起关闭,或者当 Python 子进程对象的引用计数变为零时?

提前致谢。

【问题讨论】:

  • 你为什么把它存储在myList
  • @thefourtheye 无目的。我现在知道我不应该。然而,存储在该列表中会导致大量错误,现在让我有机会更好地了解 python 的工作原理。这就是你进来的地方。别管我为什么把它放在那个地方。我关心的是了解为什么这会给我带来错误。
  • @Konos5 - 您不必将 myList 声明为 global - 如果找不到,解释器会在外部范围内找到它在本地范围内。如果您以这种方式使用 myList += self.myprocess,您将需要 global 运算符 - 但这(在大多数情况下)几乎与使用 全球
  • 如果你在 *nix 上,你试过 close_fds=True 吗?

标签: python list reference subprocess python-multithreading


【解决方案1】:

是的,del 只会删除对对象的引用,它的名称是这样的。要从列表中删除项目,您需要使用不同的语法。

>>> a = 10 
>>> l = [a]
>>> del a
>>> a                  # the name a will be gone now
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  NameError: name 'a' is not defined
>>> l                  # but the list will still contain 10
[10]
>>> del l[0]
>>> l                  # but now it is gone
[]
>>> 

或者,您可以致电l.remove(a)

【讨论】:

  • 好点。但是我现在的问题变成了:在处理子进程对象(而不是您的示例中的整数)时,文件描述符是否与实际的 os 进程一起关闭,或者当对 python 子进程对象的引用计数变为零时?
  • 与进程关联的文件描述符属于子进程对象。在原始进程终止后,可以保持打开文件。所以你需要清理子进程。
【解决方案2】:

Popen 启动一个新的 OS 进程,并返回一个可用于与之交互的 python 对象。

虽然两者还是分开的,删除python对象并不一定会影响单独运行的进程,只是删除了与之交互的便捷方式。

python Popen 对象在进程终止后继续存在是完全合理的,反之亦然。

行为将取决于 Popen 的参数,例如即使在整个脚本终止后,进程也可以配置为继续运行。

如果是一次性进程,它应该在communicate()调用之后终止,但是你应该在这个调用之后检查返回码来验证(或者使用poll()来检查它是否存在)。

终止也可能需要一些时间,例如,如果您是每秒调用此函数 1000 次,您可能启动它们的速度快于它们终止的速度,从而导致 FD 耗尽。

【讨论】:

  • 不管我调用这个函数的速率如何,这不应该导致FD耗尽吗?如果返回的与进程交互的python对象无限期地停留在myList中,fds不应该保持打开状态还是应该在OS进程返回时关闭?
  • 再次,我认为 Popen 可能取决于 args,但要确保避免任何 FD 保持打开状态,您可以调用例如myprocess.stderr.close(); myprocess.stdout.close(); myprocess.terminate() - 应该(如果我没记错的话)确保对象使用的所有 FD 都已关闭,然后可以毫无问题地让 1000 个这些 FD 挂在列表中。
  • 测试了您的建议并且工作正常。因此,我得出结论,我可以让myprocess 完成而不将其分配给最终会隐式关闭其 FD 的任何列表/变量,或者我也可以将其分配给列表/变量,但在从我的命名空间中删除它的引用之前,我应该明确关闭它FD
【解决方案3】:

我找到了一篇很好的博文,准确地描述了我所面临的情况:

How subprocess and file descriptors work in Python

通过强调 *nixes 和 Windows 在文件处理方面的差异,直观地解决了问题。作者甚至提出了解决方案!绝对值得一读。

【讨论】:

    【解决方案4】:

    在保留进程列表的同时避免 fd 耗尽的最佳方法是使用进程池。这样您就可以限制同时打开的进程数,只有在前一个进程结束时才会启动新进程。

    https://docs.python.org/3.4/library/multiprocessing.html#using-a-pool-of-workers

    当然,关于引用删除以及为什么会耗尽内存,其他两个答案都是正确的,这就是我在这里不重复的原因。

    【讨论】:

      【解决方案5】:

      您可以使用 myprocess.kill()myprocess.terminate() 来关闭正在运行的进程 - 如果您在退出时不需要它 运行() 方法。 (你的 sn-p 暗示你没有)

      如果您也没有将其添加为属性并且没有将其添加到 myList (根据您的代码,它是多余的),它会在您退出后很快被销毁 run() - 在 Python 中,您通常依赖垃圾收集器,而 del 运算符的实际使用很少

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-06
        • 2019-05-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多