带有子进程、IPC、SMP 的封闭 fd

Posted

技术标签:

【中文标题】带有子进程、IPC、SMP 的封闭 fd【英文标题】:closed fd with subprocesses, IPC, SMP 【发布时间】:2012-12-02 11:32:44 【问题描述】:

给定函数

def get_files_from_sha(sha, files):
    from subprocess import Popen, PIPE
    import tarfile
    if 0 == len(files):
        return 
    p = Popen(["git", "archive", sha], bufsize=10240, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    tar = tarfile.open(fileobj=p.stdout, mode='r|')
    p.communicate()
    contents = 
    doall = files == '*'
    if not doall:
        files = set(files)
    for entry in tar:
        if (isinstance(files, set) and entry.name in files) or doall:
            tf = tar.extractfile(entry)
            contents[entry.name] = tf.read()
            if not doall:
                files.discard(entry.name)

    if not doall:
        for fname in files:
            contents[fname] = None
    tar.close()
    return contents

在循环中调用 sha 的某些值,一段时间后(在我的例子中,4 次迭代)它开始在调用 tf.read() 时失败,并显示以下消息:

Traceback (most recent call last):
  File "../yap-analysis/extract.py", line 243, in <module>
    commits, identities, identities_by_name, identities_by_email, identities_freq = build_commits(commits)
  File "../yap-analysis/extract.py", line 186, in build_commits
    commit = get_commit(commit)
  File "../yap-analysis/extract.py", line 84, in get_commit
    contents = get_files_from_sha(commit['sha'], files)
  File "../yap-analysis/extract.py", line 42, in get_files_from_sha
    contents[entry.name] = tf.read()
  File "/usr/lib/python2.7/tarfile.py", line 817, in read
    buf += self.fileobj.read()
  File "/usr/lib/python2.7/tarfile.py", line 737, in read
    return self.readnormal(size)
  File "/usr/lib/python2.7/tarfile.py", line 746, in readnormal
    return self.fileobj.read(size)
  File "/usr/lib/python2.7/tarfile.py", line 573, in read
    buf = self._read(size)
  File "/usr/lib/python2.7/tarfile.py", line 581, in _read
    return self.__read(size)
  File "/usr/lib/python2.7/tarfile.py", line 606, in __read
    buf = self.fileobj.read(self.bufsize)


ValueError: I/O operation on closed file

我怀疑子进程尝试进行一些并行化(?)。

真正的原因是什么以及如何在 python2 上以干净和健壮的方式解决它?

【问题讨论】:

您可以将if (isinstance(files, set) and entry.name in files) or doall 更改为if doall or entry.name in files: 并为自己节省一个类型测试。 【参考方案1】:

不要在Popen 实例上使用.communicate();它将读取stdout 流直到完成。来自文档:

与进程交互:将数据发送到标准输入。从 stdout 和 stderr 读取数据,直到到达文件结尾。

.communicate() 的代码甚至在管道的stdout 上添加了一个显式的.close() 调用。

只需删除对.communicate() 的调用就足够了,但在读取 tar 文件内容之后,还要添加一个.wait()

tar.close()
p.stdout.close()
p.wait()

可能tar.close() 也关闭了p.stdout,但额外的.close() 应该不会受到伤害。

【讨论】:

【参考方案2】:

我认为你的问题是p.communicate()。此方法发送到标准输入,从标准输出和标准错误(您没有捕获)读取并等待进程终止。

tarfile 正在尝试从进程 stdout 中读取数据,而当它读取时,进程已完成,因此出现错误。

我没有尝试运行您的代码(我无权访问git),但您可能根本不想要p.communicate,请尝试将其注释掉。

【讨论】:

以上是关于带有子进程、IPC、SMP 的封闭 fd的主要内容,如果未能解决你的问题,请参考以下文章

IPC - 命名管道(fifo)- 使用

IPC - 命名管道(fifo)- 使用

IPC - 命名管道(fifo)- 使用

IPC - 命名管道(fifo)- 使用

节点 IPC 如何在 2 个进程之间工作

带有 IPC::Run 的 Perl 命令执行器