2013-02-13 4 views
2

J'essaie d'utiliser des tuyaux pour communiquer entre les processus en python. Ces processus seront appelés à partir de différents threads, et n'auront donc pas nécessairement un accès direct à l'objet Popen pour chaque processus. J'ai écrit un script ci-dessous, comme une simple preuve de concept, mais j'ai constaté que mon processus de réception ne se termine jamais.Comment fermer correctement un tube partagé par deux processus?

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'   

Cela fonctionne correctement de plusieurs façons. Les blocs de données sont redirigés vers le second processus et le deuxième processus décompresse le flux et l'écrit dans un fichier. Je peux regarder les processus jusqu'à ce qu'ils semblent attendre (et ne rien faire), terminer le 2ème processus, et le fichier semble être complètement écrit. Donc, je me demande pourquoi le 2ème processus ne se termine jamais. Il semble qu'il ne réalise jamais que le flux d'entrée a été fermé. Comment puis-je fermer le tube correctement, afin que le processus sache se terminer?

[email protected]:/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 
[email protected]:/home/tmp/db$ bg 
[1]+ python test.py & 
[email protected]:/home/tmp/db$ jobs -l 
[1]+ 31533 Running     python test.py & 
[email protected]:/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 
[email protected]:/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 
[email protected]:/home/tmp/db$ strace -p 31533 
Process 31533 attached - interrupt to quit 
wait4(31535, ^C <unfinished ...> 
Process 31533 detached 

J'imagine que je fais quelque chose de stupide. J'aimerais savoir quoi, et pourquoi.

Répondre

2

Le second processus hérite probablement de l'extrémité d'entrée du tuyau, qui ne se ferme donc jamais. Je ne suis pas un expert en Python, mais il est peut-être possible d'éviter cela en commençant par le Popen en commençant par le stdin=PIPE, puis en Popen le premier processus avec les stdout.stdin du second processus. (Popen organise sans doute pour le processus de ne pas avoir une poignée à l'extrémité d'entrée du tuyau qu'il crée en interne.)

Pour travailler autour de l'héritage de descripteur de fichier, appelez le sous-processus en utilisant close_fds=True:

pobj2 = subprocess.Popen(shlex.split(cmd2), 
         stdin=pipe_out, 
         stdout=file_out, 
         close_fds=True) 
+0

Ahah! Il s'avère donc que l'utilisation de 'close_fds = True' résout ce problème et le fait fonctionner comme prévu. – vezult

+0

@vezult Merci, je ne connaissais pas close_fds, mais c'est logique. – Neil

1

avec subprocess.Popen(), vous ne devriez pas avoir à jouer avec appeler manuellement os.pipe() et autres.

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+')) 

devrait faire ce que vous voulez.

+0

Comme décrit dans mon ticket, l'utilisation des attributs d'objet Popen n'est pas une solution, car ils ne seront pas disponibles dans des threads séparés. Je n'appelle pas 'os.popen()', donc je ne suis pas sûr de savoir de quoi vous parlez. – vezult

+0

Excuses, je voulais dire 'os.pipe()'. – Casey

+0

Je n'étais pas non plus clair sur ce que vous vouliez dire par "appeler" un processus dans votre question initiale. Si vous voulez dire que vous voulez que plusieurs threads puissent envoyer une entrée dans le pipe pour que votre sous-processus soit lu, il ne me semble pas plus difficile de transmettre pobject.stdin qu'un descripteur de fichier ou un flux que vous avez fdopen d'un descripteur. – Casey

Questions connexes