【发布时间】:2016-03-11 14:53:43
【问题描述】:
让我们考虑以下 Python 代码,由 cpython 在 Linux 系统上执行(警告:它将尝试创建或覆盖 /tmp/first、/tmp/second 和 /tmp/third 中的文件)。
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import subprocess
import os
import sys
import threading
class ThreadizedPopen(threading.Thread):
def __init__(self, command, stdin_name, stdout_name):
super(ThreadizedPopen, self).__init__()
self.command = command
self.stdin_name = stdin_name
self.stdout_name = stdout_name
self.returncode = None
def run(self):
with open(self.stdin_name, 'rb') as fin:
with open(self.stdout_name, 'wb') as fout:
popen = subprocess.Popen(self.command, stdin=fin, stdout=fout, stderr=None)
popen.communicate()
self.returncode = popen.returncode
def main():
os.system('mkfifo /tmp/first')
os.system('mkfifo /tmp/second')
os.system('mkfifo /tmp/third')
popen1 = ThreadizedPopen(['cat'], '/tmp/first', '/tmp/second')
popen2 = ThreadizedPopen(['cat'], '/tmp/second', '/tmp/third')
popen1.start()
popen2.start()
with open('/tmp/third') as fin:
print fin.read()
popen1.join()
popen2.join()
if __name__ == '__main__':
main()
然后我执行它,在另一个 shell 上,我在/tmp/first 中写了一些东西(比如echo test > /tmp/first)。我希望 Python 程序能够快速退出并打印我提供给第一个 FIFO 的相同内容。
理论上,我在/tmp/first 中编写的字符串应该会被我的程序生成的两个cat 进程复制到其他两个FIFO,然后由主Python 程序拾取并写入其标准输出。每一个cat进程一旦完成,就应该关闭它的写FIFO端,使对应的读端返回EOF并触发后面的cat进程终止。查看带有strace 的程序,可以发现测试字符串通过所有三个FIFO 正确复制,并被Python 主程序读取。第一个 FIFO 也正确关闭(并且第一个 cat 进程连同它的管理器 Python 线程一起退出)。然而,第二个cat 进程卡在read() 调用中,期望从其读取FIFO 中获取数据。
我不明白为什么会这样。从pipe(t) man page (据我所知,它也涵盖了这种 FIFO)看来,一旦写入结束(及其所有重复项)关闭,对 FIFO 的读取就会返回 EOF。根据strace,这似乎是跟踪(特别是cat 进程已死,因此其所有文件描述符都已关闭;其管理线程也已关闭其描述符,我可以在strace 中看到它输出)。
你能告诉我为什么会这样吗?如果有用,我可以发布strace 输出。
【问题讨论】: