带有子进程、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的主要内容,如果未能解决你的问题,请参考以下文章