如何正确关闭两个进程共享的管道?
我正在尝试在Python中使用管道来实现进程之间的通信。这些进程会从不同的线程中调用,因此可能无法直接访问每个进程的Popen
对象。我写了下面的脚本,作为一个简单的概念验证,但发现我的接收进程从来没有结束。
import os
import subprocess
import traceback
import shlex
if __name__ == '__main__':
(fd_out, fd_in) = os.pipe()
pipe_in = os.fdopen(fd_in, 'w')
pipe_out = os.fdopen(fd_out, 'r')
file_out = open('outfile.data', 'w+')
cmd1 = 'cat ' + ' '.join('parts/%s' % x for x in sorted(os.listdir('parts')))
cmd2 = 'pbzip2 -d -c'
pobj1 = subprocess.Popen(shlex.split(cmd1), stdout=pipe_in)
pobj2 = subprocess.Popen(shlex.split(cmd2), stdin=pipe_out,
stdout=file_out)
print 'closing pipe in'
pipe_in.close()
print 'closing pipe out'
pipe_out.close()
print 'closing file out'
file_out.close()
print 'waiting on process 2'
pobj2.wait()
print 'done'
在很多方面,这个程序运行得很正常。数据块通过管道传送到第二个进程,第二个进程解压缩数据流并将其写入文件。我可以观察这些进程,直到它们似乎只是等待(什么都不做),然后我终止第二个进程,文件似乎已经完全写入。
所以,我在想为什么第二个进程从来没有结束。它似乎没有意识到输入流已经关闭。我该如何正确关闭管道,以便进程知道要结束?
david_clymer@zapazoid:/home/tmp/db$ python test.py
closing pipe in
closing pipe out
closing file out
waiting on process 2
^Z
[1]+ Stopped python test.py
david_clymer@zapazoid:/home/tmp/db$ bg
[1]+ python test.py &
david_clymer@zapazoid:/home/tmp/db$ jobs -l
[1]+ 31533 Running python test.py &
david_clymer@zapazoid:/home/tmp/db$ ps -fp 31533
UID PID PPID C STIME TTY TIME CMD
1000 31533 22536 0 15:22 pts/2 00:00:00 python test.py
david_clymer@zapazoid:/home/tmp/db$ lsof |grep $(pwd)
bash 3432 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
bash 22536 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
python 31533 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31536 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31536 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31537 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31537 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31538 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31538 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31539 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31539 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31540 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31540 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31541 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31541 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31542 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31542 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31543 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31543 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
pbzip2 31535 31544 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
pbzip2 31535 31544 david_clymer 1u REG 253,3 12255300000 397270 /home/tmp/db/outfile.data
lsof 31599 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
grep 31600 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
lsof 31602 david_clymer cwd DIR 253,3 483328 408117 /home/tmp/db
david_clymer@zapazoid:/home/tmp/db$ strace -p 31533
Process 31533 attached - interrupt to quit
wait4(31535, ^C <unfinished ...>
Process 31533 detached
我想我可能做了什么愚蠢的事情。我想知道是什么,以及为什么。
2 个回答
1
使用 subprocess.Popen()
的时候,你不需要手动去调用 os.pipe()
这些复杂的东西。
pobj1 = subprocess.Popen(['cat'] + ['parts/' + x for x in sorted(os.listdir('parts'))],
stdout=PIPE)
pobj2 = subprocess.Popen(shlex.split('pbzip2 -d -c'),
stdin=pobj1.stdout,
stdout=open('outfile.data', 'w+'))
这样就能实现你想要的效果。
2
第二个进程可能继承了管道的输入端,这样就导致它永远不会被关闭。我不是Python方面的专家,但也许可以通过先用 Popen
启动第二个进程,并设置 stdin=PIPE
,然后再用第一个进程的 stdout
连接到第二个进程的 stdin
来避免这个问题。(Popen
可能会确保这个进程不会有对它内部创建的管道输入端的引用。)
为了避免文件描述符的继承,可以在调用子进程时使用 close_fds=True
:
pobj2 = subprocess.Popen(shlex.split(cmd2),
stdin=pipe_out,
stdout=file_out,
close_fds=True)