2013-10-18 66 views
22

我在Linux機器上運行使用subprocess.check_output(),因爲它遵循其創建一個子進程的Python腳本:如何殺死當父母死亡時用subprocess.check_output()創建的python子進程?

subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT) 

的問題是,即使父進程死亡,孩子仍在運行。 有什麼辦法可以殺死孩子的過程以及父母死亡?

+0

[這](http://stackoverflow.com/a/14128476/2235132)回答可能的幫助。 – devnull

+0

使用check_output FCT的時候,因爲我沒有得到孩子進程的PID它並不能幫助我,所以我不能殺死它... – Clara

+1

相關:如何讓家長退出後,子進程死掉? (http://stackoverflow.com/q/284325/4279)和[最好的方法殺死所有的子進程(http://stackoverflow.com/q/392022/4279) – jfs

回答

16

你的問題是使用subprocess.check_output - 你是正確的,使用的界面,你不能讓孩子PID。使用POPEN代替:

proc = subprocess.Popen(["ls", "-l"], stdout=PIPE, stderr=PIPE) 

# Here you can get the PID 
global child_pid 
child_pid = proc.pid 

# Now we can wait for the child to complete 
(output, error) = proc.communicate() 

if error: 
    print "error:", error 

print "output:", output 

要確保你殺了孩子退出:

import os 
import signal 
def kill_child(): 
    if child_pid is None: 
     pass 
    else: 
     os.kill(child_pid, signal.SIGTERM) 

import atexit 
atexit.register(kill_child) 
+0

好的,我沒有意識到溝通()實際上等待子進程完成。只需要一個返回輸出和錯誤的fct並等待子進程完成,並且在父進程結束時殺死子進程。 – Clara

+1

如果父進程崩潰,那麼不能保證會調用'atexit'處理程序。順便說一句,如果你有'proc'對象;你可以直接調用'proc.kill()'(不需要首先提取pid) – jfs

+1

@ J.F。塞巴斯蒂安:如果父母崩潰,那麼任何機制都可能不會被調用,除非它是由第三方完成的(然後可能會崩潰)。必須承認,我忘了'proc.kill()' - 好點。 – cdarke

-2

手動你可以這樣做:

ps aux | grep <process name>

得到PID(第二列)和

kill -9 <PID> -9被強行殺死它

+0

我希望能夠做到它以編程方式,通過獲取子進程的PID,或通過其他技巧。另外我甚至無法手動殺死孩子,因爲我沒有它的PID。 – Clara

+0

它與上面註釋中的鏈接相同 - 使用Popen你可以獲得子進程的PID,而當我使用check_output時沒有。 – Clara

+1

@Clara你也可以使用'Popen'獲得輸出,'subprocess.Popen(...)。communicate()[0]'。 –

1

不知道的細節,但最好的方法仍然是用信號捕捉錯誤(甚至可能是所有錯誤)並終止其中的任何剩餘進程。

import signal 
import sys 
import subprocess 
import os 

def signal_handler(signal, frame): 
    sys.exit(0) 
signal.signal(signal.SIGINT, signal_handler) 

a = subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT) 

while 1: 
    pass # Press Ctrl-C (breaks the application and is catched by signal_handler() 

這只是一個小樣,你需要捕捉的不僅僅是SIGINT以上,但這個想法可能會得到你開始,你需要檢查生成的進程仍莫名其妙。

http://docs.python.org/2/library/os.html#os.kill http://docs.python.org/2/library/subprocess.html#subprocess.Popen.pid http://docs.python.org/2/library/subprocess.html#subprocess.Popen.kill

我建議重寫check_output原因個性化的版本,我才意識到check_output實際上只是簡單調試等,因爲你不能在執行過程中進行交互這麼多吧..

重寫check_output:

from subprocess import Popen, PIPE, STDOUT 
from time import sleep, time 

def checkOutput(cmd): 
    a = Popen('ls -l', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT) 
    print(a.pid) 
    start = time() 
    while a.poll() == None or time()-start <= 30: #30 sec grace period 
     sleep(0.25) 
    if a.poll() == None: 
     print('Still running, killing') 
     a.kill() 
    else: 
     print('exit code:',a.poll()) 
    output = a.stdout.read() 
    a.stdout.close() 
    a.stdin.close() 
    return output 

,做任何你想用它一樣,PE rhaps將活動執行存儲在一個臨時變量中,並在退出時用信號或其他方式來阻止主循環的錯誤/關閉。

最後,您仍然需要在主應用程序中捕捉終端以便安全地殺死任何孩子,最好的方法是使用try & exceptsignal

20

是的,你可以通過兩種方法實現這一點。他們都要求您使用Popen而不是check_output。首先是一個簡單的方法,使用try..finally如下:

from contextlib import contextmanager 

@contextmanager 
def run_and_terminate_process(*args, **kwargs): 
try: 
    p = subprocess.Popen(*args, **kwargs) 
    yield p   
finally: 
    p.terminate() # send sigterm, or ... 
    p.kill()  # send sigkill 

def main(): 
    with run_and_terminate_process(args) as running_proc: 
     # Your code here, such as running_proc.stdout.readline() 

這會趕上SIGINT(鍵盤中斷)和SIGTERM,而不是SIGKILL(如果你用-9殺死你的腳本)。

另一種方法有點複雜,並且使用ctypes的prctl PR_SET_PDEATHSIG。一旦父母因任何原因退出(甚至是sigkill),系統會向孩子發送信號。

import signal 
import ctypes 
libc = ctypes.CDLL("libc.so.6") 
def set_pdeathsig(sig = signal.SIGTERM): 
    def callable(): 
     return libc.prctl(1, sig) 
    return callable 
p = subprocess.Popen(args, preexec_fn = set_pdeathsig(signal.SIGTERM)) 
+0

如果你需要的命令的輸出,你可以用'p.stdout.read()'或'p.stdout.readlines()' – micromoses

+1

一號方法只是開始,*立即*殺死子,你應該加上'在'Popen()'調用之後的p.communicate()/ p.wait()'。 [第2方法僅適用於Linux的(http://www.evans.io/posts/killing-child-processes-on-parent-exit-prctl/) – jfs

+0

@JF,你是對的,這是不明朗我的例子是程序的代碼應該在'finally'語句之前出現,並且正確地縮進了'try' ..我認爲它現在更清晰了。無論如何,這個問題並不清楚,如果父母可能會在孩子面前死去(除非被殺死),所以我不能認爲實際的命令是'ls',或者應該等待孩子(可能是某種服務器?)。至於你的第二個評論,這個問題指出該系統是Linux。 – micromoses