我在 subprocess
documentation 中找到了这段代码,其中一个进程的标准输出被管道传输到另一个进程:
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]
我对 stdout.close()
调用感到困惑。关闭 stdout 句柄肯定会阻止进程产生任何输出吗?
所以我进行了一个实验,令我惊讶的是这个过程根本没有受到它的影响:
from subprocess import Popen, PIPE
p1 = Popen(['python', '-c', 'import time; time.sleep(5); print(1)'], stdout=PIPE)
p2 = Popen(['python', '-c', 'print(input()*3)'], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
print('stdout closed')
print('stdout:', p2.communicate()[0])
# output:
# stdout closed
# [5 second pause]
# stdout: b'111\n'
这是怎么回事?为什么进程可以写入一个关闭的管道?
最佳答案
关闭文件描述符只是意味着减少引用计数(在操作系统内核中)。描述符编号变为无效,但除非引用计数为零,否则它所引用的对象不会发生任何事情。
在 Popen
调用中,正在执行复制文件描述符的操作,从而增加引用计数:像 dup
/dup2
这样的操作和 fork
。
如果我们fork
一个从父进程接收文件描述符的子进程,则该文件描述符是指向同一个对象的副本。
如果父级关闭该描述符的原始副本,则不会影响子级中的那个。反之亦然。只有父子双方都关闭了那个描述符,底层打开的文件/设备对象才会消失;并且仅当没有其他描述符引用它时。
即使描述符具有相同的编号也是如此;每个进程都有自己的文件描述符编号表。子文件描述符 3 与父文件描述符 3 不同。
关于python - 为什么子进程在关闭后仍然可以写入标准输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49719673/