当我用python子进程Popen启动进程时,如何关闭stdout-pipe?

问题描述:

我想知道是否有可能关闭通信管道时,杀死一个不同的线程启动的子进程。如果我不调用通信(),那么kill()将按预期工作,在一秒钟而不是五秒钟后终止进程。当我用python子进程Popen启动进程时,如何关闭stdout-pipe?

我发现了类似问题here的讨论,但我没有得到真正的答案。我假设我必须能够关闭管道或者明确地终止子子进程(在本例中为“睡眠”)并杀死它以释放管道。

我也尝试在SO上找到她的答案,但我只找到thisthisthis,根据我可以说(?),它并不直接解决此问题。

所以我想要做的事情是能够在第二个线程中运行一个命令并获取其所有输出,但是当我渴望时能够立即杀死它。我可以通过一个文件和尾部或类似的,但我认为应该有一个更好的方式来做到这一点?

import subprocess, time 
from threading import Thread 

process = None 

def executeCommand(command, runCommand): 
    Thread(target=runCommand, args=(command,)).start() 

def runCommand(command): 
    global process 
    args = command.strip().split() 
    process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE) 

    for line in process.communicate(): 
     if line: 
      print "process:", line, 

if __name__ == '__main__': 
    executeCommand("./ascript.sh", runCommand) 
    time.sleep(1) 
    process.kill() 

这是脚本:

#!/bin/bash 
echo "sleeping five" 
sleep 5 
echo "slept five" 

输出

$ time python poc.py 
process: sleeping five 

real 0m5.053s 
user 0m0.044s 
sys 0m0.000s 
+0

当'process.kill()'被调用时,'/ ascript.sh'死亡,但'睡眠5'里面没有。有趣的是,在'。/ ascript.sh'死后,Python不会立即返回。 – 2009-10-26 11:47:07

我认为问题在于process.kill()只会杀死直接子进程(bash),而不会杀死bash脚本的子进程。

的问题和解决方案描述如下:

使用POPEN(...,preexec_fn = os.setsid)来创建一个进程组和OS。 pgkill杀死整个进程组。例如

import os 
import signal 
import subprocess 
import time 
from threading import Thread 

process = None 

def executeCommand(command, runCommand): 
    Thread(target=runCommand, args=(command,)).start() 

def runCommand(command): 
    global process 
    args = command.strip().split() 
    process = subprocess.Popen(
     args, shell=False, stdout=subprocess.PIPE, preexec_fn=os.setsid) 

    for line in process.communicate(): 
     if line: 
      print "process:", line, 

if __name__ == '__main__': 
    executeCommand("./ascript.sh", runCommand) 
    time.sleep(1) 
    os.killpg(process.pid, signal.SIGKILL) 

$ time python poc.py 
process: sleeping five 

real 0m1.051s 
user 0m0.032s 
sys 0m0.020s 

看起来你可能是Python的超粗粒度并行的受害者。更改你的脚本是:

#!/bin/bash 
echo "sleeping five" 
sleep 5 
echo "sleeping five again" 
sleep 5 
echo "slept five" 

然后输出变为:

process: sleeping five 

real 0m5.134s 
user 0m0.000s 
sys  0m0.010s 

如果整个脚本运行的时间将是10秒。所以看起来python控制线程在bash脚本休眠之后才会真正运行。同样,如果你改变你的脚本是:

#!/bin/bash 
echo "sleeping five" 
sleep 1 
sleep 1 
sleep 1 
sleep 1 
sleep 1 
echo "slept five" 

然后输出变为:

process: sleeping five 

real 0m1.150s 
user 0m0.010s 
sys  0m0.020s 

总之,你的代码工作逻辑上实现。 :)

+0

示例中的睡眠只是在脚本中执行任何冗长操作的占位符。问题是,如何让管道关闭,以便代码不会挂在通信()上?或者,我如何找出哪些“子子进程”要杀死以释放块? – icecream 2009-10-27 06:57:19

+0

你的意思是说全球口译员锁定在工作中很难吗? http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock – 2009-10-27 07:09:43

+0

可能是?有什么建议么? – icecream 2009-10-27 09:59:52

在我看来,最简单的方法来做到这一点,并避免多线程问题将是从主线程设置kill标志,并在通信之前在脚本运行线程中检查它,查杀当该标志是True时的脚本。

+0

实际上,它与阻塞管道有关的问题多于多线程问题。我需要一个不同的线程,以便我可以从某个地方关闭它。如果我从shell中“杀死”它会杀死脚本及其子进程。 Python的kill失败了,因为它似乎希望管道关闭。 – icecream 2009-10-27 06:59:26