Die Gewährleistung Teilprozesse sind tot beim Verlassen Python-Programm
-
11-07-2019 - |
Frage
Gibt es eine Möglichkeit alle geschaffen subprocess, um sicherzustellen, sind tot an der Ausfahrt Zeit eines Programms Python? Durch subprocess meine ich diejenigen mit subprocess.Popen () erstellt wurde.
Wenn nicht, sollte ich iterieren alle die Ausgabe kills über und dann tötet -9? etwas sauberer?
Lösung
Sie können mit atexit für diese, und registrieren jede aufzuräumen Aufgaben ausgeführt werden, wenn das Programm beendet wird.
atexit.register (func [* args [** Kargs]])
In Ihrem Bereinigungsprozess, können Sie auch Ihre eigene Warte implementieren, und es töten, wenn ein gewünschter Timeout auftritt.
>>> import atexit
>>> import sys
>>> import time
>>>
>>>
>>>
>>> def cleanup():
... timeout_sec = 5
... for p in all_processes: # list of your processes
... p_sec = 0
... for second in range(timeout_sec):
... if p.poll() == None:
... time.sleep(1)
... p_sec += 1
... if p_sec >= timeout_sec:
... p.kill() # supported from python 2.6
... print 'cleaned up!'
...
>>>
>>> atexit.register(cleanup)
>>>
>>> sys.exit()
cleaned up!
Hinweis -. Registrierte Funktionen werden nicht ausgeführt werden, wenn dieser Prozess (Eltern-Prozess) getötet wird,
Die folgende Fenster Methode wird nicht mehr benötigt für Python> = 2.6
Hier ist ein Weg, um einen Prozess in den Fenstern zu töten. Ihr Popen Objekt hat ein pid Attribut, so dass Sie es einfach anrufen können durch Erfolg = win_kill (p.pid) (Needs pywin32 installiert ist):
def win_kill(pid):
'''kill a process by specified PID in windows'''
import win32api
import win32con
hProc = None
try:
hProc = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
win32api.TerminateProcess(hProc, 0)
except Exception:
return False
finally:
if hProc != None:
hProc.Close()
return True
Andere Tipps
Auf * nichts ist, vielleicht Prozessgruppen verwenden, können Ihnen helfen, -. Sie Subprozesse gelaicht von Ihrem Subprozesse auch fangen können
if __name__ == "__main__":
os.setpgrp() # create new process group, become its leader
try:
# some code
finally:
os.killpg(0, signal.SIGKILL) # kill all processes in my group
Eine weitere Überlegung ist, die Signale zu eskalieren: von SIGTERM (Standardsignal für kill
) zu SIGKILL (a.k.a kill -9
). Warten Sie eine kurze Zeit zwischen den Signalen dem Prozess eine Chance zu geben, sauber zu beenden, bevor Sie es kill -9
.
Die subprocess.Popen.wait()
ist der einzige Weg, um sicherzustellen, dass sie tot sind. Tatsächlich erfordern POSIX OS, dass Sie auf Ihre Kinder warten. Viele * nix ist ein „Zombie“ Prozess erstellen. Ein totes Kind, für die die Eltern nicht warten
Wenn das Kind einigermaßen gut geschrieben, es endet. Oft lesen Kinder von PIPE. der Eingang geschlossen ist ein großer Hinweis auf das Kind, dass es sollte den Laden und Ausgang schließen.
Wenn das Kind Fehler hat und nicht beendet, können Sie es töten. Sie sollen diese Fehler beheben.
Wenn das Kind eine „Serve-forever“ Schleife und ist nicht zu beenden, entworfen, sollten Sie entweder töten oder eine Eingabe oder eine Nachricht, die es zwingen werden, zu beenden.
Bearbeiten.
Im Standard-OS haben Sie os.kill( PID, 9 )
. Kill -9 ist hart, BTW. Wenn man sie mit SIGABRT töten kann (6?) Oder SIGTERM (15), das ist höflicher.
In Windows OS, Sie keine os.kill
haben, die funktioniert. Schauen Sie sich diese Activestate Rezept für einen Prozess in Windows beendet wird.
Wir haben untergeordnete Prozesse, die WSGI-Server sind. So beenden wir ihnen eine GET auf einer speziellen URL tun; Dies bewirkt, dass das Kind zu säubern und zu beenden.
Achtung: Linux-only! Sie können Ihr Kind erhält ein Signal, wenn seine Eltern sterben machen.
Erste Python-prctl == 1.5.0 dann Ihre Eltern Code ändern Sie Ihr Kind-Prozesse zu starten wie folgt
installierensubprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))
Was dies sagt, ist:
- Start subprocess: Schlaf 100
- nach gabeln und vor exec des subprocess, das Kind für Register „schicken Sie mir ein SIGKILL wenn meine Eltern beendet“.
poll ()
Überprüfen Sie, ob Kind-Prozess beendet wurde. Returns Return Attribut.
Ich brauchte eine kleine Variation dieses Problems (Subprozesse Aufräumen, aber ohne das Python-Programm selbst verläßt), und da es hier nicht unter den anderen Antworten erwähnt:
p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
setsid
wird das Programm in einer neuen Sitzung ausgeführt werden, so dass eine neue Prozessgruppe sie und ihre Kinder zuweisen. Aufruf os.killpg
an wird es also nicht, Ihren eigenen Python-Prozess bringt auch.
oriP Antwort ist hilfreich, hat aber den Nachteil, dass es Ihren Prozess tötet und gibt einen Fehlercode Ihre Eltern. Ich vermied es, dass wie folgt aus:
class CleanChildProcesses:
def __enter__(self):
os.setpgrp() # create new process group, become its leader
def __exit__(self, type, value, traceback):
try:
os.killpg(0, signal.SIGINT) # kill all processes in my group
except KeyboardInterrupt:
# SIGINT is delievered to this process as well as the child processes.
# Ignore it so that the existing exception, if any, is returned. This
# leaves us with a clean exit code if there was no exception.
pass
Und dann:
with CleanChildProcesses():
# Do your work here
Natürlich können Sie dies mit try / except / finally, aber Sie haben die außergewöhnlichen und nicht-Ausnahmefällen separat zu behandeln.
Gibt es eine Möglichkeit alle geschaffen subprocess, um sicherzustellen, sind tot an der Ausfahrt Zeit eines Programms Python? Durch subprocess meine ich diejenigen mit subprocess.Popen () erstellt wurde.
Sie könnten Verkapselung verletzen und Test , dass alle Popen Prozesse beendet haben, indem Sie
subprocess._cleanup()
print subprocess._active == []
Wenn nicht, sollte ich iterieren alle die Ausgabe kills über und dann tötet -9? etwas sauberer?
Sie können nicht gewährleisten, dass alle Teilprozesse tot sind ohne jeden Überlebenden zu gehen und zu töten. Aber wenn Sie dieses Problem haben, ist es wahrscheinlich, weil Sie ein tieferes Design-Problem haben.
Eine Lösung für Fenster sein, um den win32 Job api zum Beispiel zu verwenden Wie zerstöre ich automatisch untergeordnete Prozesse in Windows?
Hier ist eine bestehende Python-Implementierung
Ich brauchte eigentlich, dies zu tun, aber es beteiligt Remote-Befehle ausgeführt werden. Wir wollten die Prozesse in der Lage sein zu stoppen, indem die Verbindung zum Server zu schließen. Auch wenn zum Beispiel Sie in der Python-repl ausführen, können Sie wählen, als Vordergrund laufen, wenn Sie in der Lage sein wollen, Ctrl-C zu verwenden, um zu beenden.
import os, signal, time
class CleanChildProcesses:
"""
with CleanChildProcesses():
Do work here
"""
def __init__(self, time_to_die=5, foreground=False):
self.time_to_die = time_to_die # how long to give children to die before SIGKILL
self.foreground = foreground # If user wants to receive Ctrl-C
self.is_foreground = False
self.SIGNALS = (signal.SIGHUP, signal.SIGTERM, signal.SIGABRT, signal.SIGALRM, signal.SIGPIPE)
self.is_stopped = True # only call stop once (catch signal xor exiting 'with')
def _run_as_foreground(self):
if not self.foreground:
return False
try:
fd = os.open(os.ctermid(), os.O_RDWR)
except OSError:
# Happens if process not run from terminal (tty, pty)
return False
os.close(fd)
return True
def _signal_hdlr(self, sig, framte):
self.__exit__(None, None, None)
def start(self):
self.is_stopped = False
"""
When running out of remote shell, SIGHUP is only sent to the session
leader normally, the remote shell, so we need to make sure we are sent
SIGHUP. This also allows us not to kill ourselves with SIGKILL.
- A process group is called orphaned when the parent of every member is
either in the process group or outside the session. In particular,
the process group of the session leader is always orphaned.
- If termination of a process causes a process group to become orphaned,
and some member is stopped, then all are sent first SIGHUP and then
SIGCONT.
consider: prctl.set_pdeathsig(signal.SIGTERM)
"""
self.childpid = os.fork() # return 0 in the child branch, and the childpid in the parent branch
if self.childpid == 0:
try:
os.setpgrp() # create new process group, become its leader
os.kill(os.getpid(), signal.SIGSTOP) # child fork stops itself
finally:
os._exit(0) # shut down without going to __exit__
os.waitpid(self.childpid, os.WUNTRACED) # wait until child stopped after it created the process group
os.setpgid(0, self.childpid) # join child's group
if self._run_as_foreground():
hdlr = signal.signal(signal.SIGTTOU, signal.SIG_IGN) # ignore since would cause this process to stop
self.controlling_terminal = os.open(os.ctermid(), os.O_RDWR)
self.orig_fore_pg = os.tcgetpgrp(self.controlling_terminal) # sends SIGTTOU to this process
os.tcsetpgrp(self.controlling_terminal, self.childpid)
signal.signal(signal.SIGTTOU, hdlr)
self.is_foreground = True
self.exit_signals = dict((s, signal.signal(s, self._signal_hdlr))
for s in self.SIGNALS)
def stop(self):
try:
for s in self.SIGNALS:
#don't get interrupted while cleaning everything up
signal.signal(s, signal.SIG_IGN)
self.is_stopped = True
if self.is_foreground:
os.tcsetpgrp(self.controlling_terminal, self.orig_fore_pg)
os.close(self.controlling_terminal)
self.is_foreground = False
try:
os.kill(self.childpid, signal.SIGCONT)
except OSError:
"""
can occur if process finished and one of:
- was reaped by another process
- if parent explicitly ignored SIGCHLD
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
- parent has the SA_NOCLDWAIT flag set
"""
pass
os.setpgrp() # leave the child's process group so I won't get signals
try:
os.killpg(self.childpid, signal.SIGINT)
time.sleep(self.time_to_die) # let processes end gracefully
os.killpg(self.childpid, signal.SIGKILL) # In case process gets stuck while dying
os.waitpid(self.childpid, 0) # reap Zombie child process
except OSError as e:
pass
finally:
for s, hdlr in self.exit_signals.iteritems():
signal.signal(s, hdlr) # reset default handlers
def __enter__(self):
if self.is_stopped:
self.start()
def __exit__(self, exit_type, value, traceback):
if not self.is_stopped:
self.stop()
Dank Malcolm Handley für den ersten Entwurf. Fertig mit python2.7 auf Linux.
eine Lösung für Linux finden Sie (ohne die Installation prctl):
def _set_pdeathsig(sig=signal.SIGTERM):
"""help function to ensure once parent process exits, its childrent processes will automatically die
"""
def callable():
libc = ctypes.CDLL("libc.so.6")
return libc.prctl(1, sig)
return callable
subprocess.Popen(your_command, preexec_fn=_set_pdeathsig(signal.SIGTERM))
Das ist das, was ich für meinen Posix-App tat:
Wenn Ihre App existiert die kill () -Methode dieser Klasse aufrufen: http://www.pixelbeat.org/libs/subProcess.py
Beispiel hier verwenden: http://code.google.com/p/ fslint / source / browse / trunk / fslint-gui # 608
Hilfe für Python-Code: http://docs.python.org/dev/library/subprocess .html # subprocess.Popen.wait
Sie können versuchen, subalive
, ein Paket, das ich für ähnliches Problem geschrieben haben. Es nutzt Routine- Ping über RPC und der Slave-Prozess endet automatisch, wenn der Meister am Leben Pings aus irgendeinem Grund stehen bleibt.
https://github.com/waszil/subalive
Beispiel für Master:
from subalive import SubAliveMaster
# start subprocess with alive keeping
SubAliveMaster(<path to your slave script>)
# do your stuff
# ...
Beispiel für Slave subprocess:
from subalive import SubAliveSlave
# start alive checking
SubAliveSlave()
# do your stuff
# ...