2013-10-18 1 views
22

ich einen Python-Skript auf einem Linux-Rechner laufen lasse, die einen untergeordneten Prozess mit subprocess.check_output erzeugt() wie folgt: dass selbst wirdWie wird ein Python-Kindprozess beendet, der mit subprocess.check_output() erstellt wurde, wenn der Elternteil stirbt?

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

Das Problem, wenn der Laufübergeordnete Prozess stirbt, wird das Kind noch . Gibt es eine Möglichkeit, den Kindprozess auch zu töten, wenn der Elternteil stirbt?

+0

[Dies] (http://stackoverflow.com/a/14128476/2235132) Antwort helfen könnte. – devnull

+0

Es hilft mir nicht, wenn Sie die check_output fct verwenden, weil ich nicht die pid des Child-Prozesses bekomme, daher kann ich es nicht töten ... – Clara

+1

related: [Wie man Child Prozess sterben nach Eltern-Exits? ] (http://stackoverflow.com/q/284325/4279) und [Beste Methode, alle Kindprozesse zu töten] (http://stackoverflow.com/q/392022/4279) – jfs

Antwort

16

Ihr Problem ist subprocess.check_output bei der Verwendung - Sie richtig sind, können Sie nicht das Kind PID bekommen diese Schnittstelle verwenden.Verwenden Popen statt:

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 

Um sicherzustellen, dass Sie das Kind beim Verlassen töten:

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, ich habe nicht bemerkt, dass communicate() tatsächlich darauf wartet, dass der Kindprozess beendet wird. Benötigt nur ein fct, das die Ausgabe und den Fehler zurückgibt und darauf wartet, dass der Unterprozess beendet wird. Außerdem wird das Kind getötet, wenn der Elternteil stirbt. – Clara

+1

Wenn der Elternteil abstürzt, gibt es keine Garantie, dass 'atexit'-Handler aufgerufen werden. BTW, wenn Sie 'proc' Objekt haben; Sie könnten 'proc.kill()' direkt aufrufen (das Pid muss nicht zuerst extrahiert werden) – jfs

+1

@ J.F. Sebastian: Wenn der Elternteil abstürzt, dann wird JEDER Mechanismus möglicherweise nicht aufgerufen, es sei denn, er wird von einem Dritten ausgeführt (und dann stürzt er ab). Muss zugeben, dass ich 'proc.kill()' vergessen habe - guter Punkt. – cdarke

-2

Manuell können Sie dies tun:

ps aux | grep <process name>

die PID (zweite Spalte) erhalten und

kill -9 <PID> -9 zu zwingen, tötet es

+0

Ich hatte gehofft zu können Es ist programmatisch, entweder durch die PID des Kind-Prozesses, oder durch andere Tricks. Außerdem kann ich das Kind nicht einmal manuell töten, weil ich seine PID nicht habe. – Clara

+0

Es ist der gleiche Link wie der oben im Kommentar - mit Popen bekommst du die PID des Child-Prozesses zurück, während ich das nicht habe, wenn ich check_output verwende. – Clara

+1

@Clara Sie können die Ausgabe auch mit 'Popen' erreichen,' subprocess.Popen (...). Communicate() [0] '. –

1

Sie wissen nicht, die Besonderheiten , aber der beste Weg ist immer noch, Fehler (und vielleicht sogar alle Fehler) mit dem Signal zu fangen und alle verbleibenden Prozesse dort zu beenden.

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

Dies ist nur ein Mock-up, würden Sie mehr als nur SIGINT fangen müssen, aber die Idee könnte Ihnen den Einstieg und Sie würden für erzeugte Prozess überprüfen müssen irgendwie noch.

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

Ich würde empfehlen, eine personalisierte Version von check_output Ursache Umschreiben, wie ich mit ihm nur check_output realisiert ist wirklich nur für einfache Fehlersuche usw. interagieren, da Sie nicht so viel während der Ausführung ..

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 

und tun, was Sie mit ihm möchten, pe Sie speichern die aktiven Ausführungen in einer temporären Variablen und töten sie beim Beenden mit einem Signal oder anderen Mitteln, um Fehler/Abschaltungen der Hauptschleife zu erfassen.

Am Ende müssen Sie noch Abschlüsse in der Hauptanwendung, um zu fangen, um sicher alle Childs zu töten, der beste Weg, um diesen Ansatz ist mit try & except oder signal.

20

Ja, Sie können dies auf zwei Arten erreichen. Beide erfordern, dass Sie Popen anstelle von check_output verwenden. Die erste ist eine einfachere Methode, mit try..finally wie folgt:

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

Diese SIGINT fangen (Tastatur-Interrupt) und SIGTERM, aber nicht SIGKILL (wenn Sie Ihr Skript mit -9 töten).

Die andere Methode ist ein bisschen komplexer, und verwendet Ctypes Prctl PR_SET_PDEATHSIG. Das System sendet ein Signal an das Kind, sobald das Elternteil aus irgendeinem Grund (sogar Sigill) beendet wird.

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

Wenn Sie die Ausgabe des Befehls benötigen, können Sie 'p.stdout.read()' oder 'p.stdout.readlines()' – micromoses

+1

verwenden, die erste Methode startet und * sofort * den Unterprozess, Sie sollten 'hinzufügen p.communicate()/p.wait() 'nach' Popen() 'call. [Die 2. Methode funktioniert nur unter Linux] (http://www.evans.io/posts/killing-child-processes-on-parent-exit-prctl/) – jfs

+0

@JF, Sie haben Recht, es war nicht klar aus mein Beispiel, dass der Code des Programms direkt vor der "finally" -Anweisung stehen sollte, mit richtiger Einrückung zum "try". Ich denke, es ist jetzt klarer. Wie auch immer, es ist nicht klar aus der Frage, wie der Elternteil vor dem Kind sterben kann (wenn er nicht getötet wird), so kann ich nicht davon ausgehen, dass der eigentliche Befehl "ls" ist oder dass auf das Kind gewartet werden sollte Server?). Wie für Ihren zweiten Kommentar, besagt die Frage, dass das System Linux ist. – micromoses