2013-10-18 4 views
22

Sono in esecuzione su una macchina Linux uno script Python che crea un processo figlio utilizzando subprocess.check_output() come segue:Come uccidere un processo figlio python creato con subprocess.check_output() quando il genitore muore?

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

Il problema è che, anche se il processo padre muore, il bambino è ancora in esecuzione . C'è un modo in cui posso uccidere il processo figlio anche quando il genitore muore?

+0

[Questo] (http://stackoverflow.com/a/14128476/2235132) risposta potrebbe aiutare. – devnull

+0

Non mi aiuta quando uso il controllo check_output fct, perché non ottengo il pid del processo figlio, quindi non posso ucciderlo ... – Clara

+1

correlato: [Come rendere il processo figlio morire dopo l'uscita del genitore? ] (http://stackoverflow.com/q/284325/4279) e [Il modo migliore per uccidere tutti i processi figlio] (http://stackoverflow.com/q/392022/4279) – jfs

risposta

16

Il tuo problema è con l'utilizzo di subprocess.check_output - lei ha ragione, non è possibile ottenere il bambino PID utilizzando tale interfaccia.Uso Popen invece:

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 

Per essere sicuri di uccidere il bambino in uscita:

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

OK, non mi sono reso conto che communicate() in realtà aspetta che il processo figlio finisca. Ho solo bisogno di un fct che restituisca l'output e l'errore e aspetti che il sottoprocesso finisca, più uccide il bambino quando il genitore muore. – Clara

+1

Se il genitore si arresta in modo anomalo, non vi è alcuna garanzia che i gestori di 'atexit' vengano chiamati. btw, se hai oggetto 'proc'; potresti chiamare 'proc.kill()' direttamente (non è necessario prima estrarre il pid) – jfs

+1

@ J.F. Sebastian: Se il genitore si arresta in modo anomalo, è possibile che QUALSIASI meccanismo non venga richiamato, a meno che non venga eseguito da una terza parte (e quindi potrebbe verificarsi un arresto anomalo). Devo ammettere che mi sono dimenticato di 'proc.kill()' - buon punto. – cdarke

-2

Manualmente si potrebbe fare questo:

ps aux | grep <process name>

ottenere il PID (seconda colonna) e

kill -9 <PID> -9 è quello di forzare ucciderlo

+0

speravo di poterlo fare programmaticamente, ottenendo il pid del processo figlio o altri trucchi. Inoltre non posso nemmeno uccidere il bambino manualmente, perché non ho il suo pid. – Clara

+0

È lo stesso link di quello nel commento sopra - con Popen si ottiene il pid del processo figlio, mentre non lo uso quando utilizzo check_output. – Clara

+1

@Clara È possibile ottenere l'output usando 'Popen' anche,' sottoprocesso.Popen (...). Communicate() [0] '. –

1

Non conosco le specifiche , ma il modo migliore è ancora di rilevare gli errori (e forse anche tutti gli errori) con il segnale e terminare qualsiasi processo rimanente.

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() 

Questo è solo un mockup, avresti bisogno di catturare più di un semplice SIGINT ma l'idea potrebbe iniziare e avresti bisogno di verificare la presenza di processo generato in qualche modo ancora.

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

Mi consiglia di riscrivere una versione personalizzata di check_output causa come ho appena realizzato check_output è in realtà solo per il semplice ecc debug dal momento che non è possibile interagire tanto con esso durante l'esecuzione di ..

Rewrite 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 

e fare quello vuoi con esso, pe raccoglie le esecuzioni attive in una variabile temporanea e le uccide all'uscita con il segnale o altri mezzi di errore/arresto del ciclo principale.

Alla fine, è ancora necessario prendere le terminazioni nell'applicazione principale per poter uccidere in sicurezza qualsiasi bambino, il modo migliore per avvicinarsi a questo è con try & except o signal.

20

Sì, è possibile ottenere questo con due metodi. Entrambi richiedono l'utilizzo di Popen anziché di check_output. Il primo è un metodo più semplice, utilizzando try..finally, come segue:

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() 

Questa cattura SIGINT (tastiera interrupt) e SIGTERM, ma non SIGKILL (se uccidi il tuo script con -9).

L'altro metodo è un po 'più complesso e utilizza il prtl PR_SET_PDEATHSIG di ctypes. Il sistema invierà un segnale al bambino una volta che il genitore esce per qualsiasi motivo (anche 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

Se hai bisogno dell'output del comando, puoi usare 'p.stdout.read()' o 'p.stdout.readlines()' – micromoses

+1

il primo metodo inizia e * immediatamente * uccide il sottoprocesso, dovresti aggiungere ' p.communicate()/p.wait() 'dopo la chiamata' Popen() '. [Il secondo metodo funziona solo su Linux] (http://www.evans.io/posts/killing-child-processes-on-parent-exit-prctl/) – jfs

+0

@JF, hai ragione, non era chiaro da il mio esempio che il codice del programma dovrebbe venire subito prima dell'istruzione 'finally', con il giusto indentation al' try' .. Penso che sia più chiaro ora. Ad ogni modo, non è chiaro dalla domanda come il genitore possa morire prima del bambino (a meno che non sia ucciso), quindi non posso assumere che il comando sia "ls", o che il bambino debba essere aspettato (potrebbe essere una sorta di server?). Per quanto riguarda il tuo secondo commento, la domanda afferma che il sistema è Linux. – micromoses