interprete - funcion cerrar programa python
Asegurar que los subprocesos estén muertos al salir del programa Python (13)
¿Hay alguna manera de garantizar que todos los subprocesos creados estén muertos en el momento de la salida de un programa de Python? Por subproceso me refiero a los creados con subproceso.Popen ().
Podría violar la encapsulación y probar que todos los procesos de Popen han terminado al hacerlo
subprocess._cleanup()
print subprocess._active == []
Si no, ¿debería iterar sobre todas las muertes emitidas y luego mata a -9? algo más limpio?
No puede asegurarse de que todos los subprocesos estén muertos sin salir y matar a todos los sobrevivientes. Pero si tiene este problema, es probablemente porque tiene un problema de diseño más profundo.
¿Hay alguna manera de garantizar que todos los subprocesos creados estén muertos en el momento de la salida de un programa de Python? Por subproceso me refiero a los creados con subproceso.Popen ().
Si no, ¿debería iterar sobre todas las muertes emitidas y luego mata a -9? algo más limpio?
encuesta( )
Verifique si el proceso hijo ha terminado. Devuelve el atributo del código de retorno.
El subprocess.Popen.wait()
es la única manera de asegurar que están muertos. De hecho, los sistemas operativos POSIX requieren que esperes a tus hijos. Muchos * nix crearán un proceso "zombie": un niño muerto para el cual el padre no esperó.
Si el niño está razonablemente bien escrito, termina. A menudo, los niños leen de PIPE. Cerrar la entrada es una gran sugerencia para el niño de que debe cerrar la tienda y salir.
Si el niño tiene errores y no termina, puede que tenga que matarlo. Deberías arreglar este error.
Si el niño es un ciclo de "servir para siempre" y no está diseñado para terminar, debe matarlo o proporcionar alguna entrada o mensaje que lo obligue a terminar.
Editar.
En los sistemas operativos estándar, tiene os.kill( PID, 9 )
. Kill -9 es duro, por cierto. Si puedes matarlos con SIGABRT (6?) O SIGTERM (15) eso es más educado.
En el sistema operativo Windows, no tienes un os.kill
que funcione. Mire esta receta de ActiveState para terminar un proceso en Windows.
Tenemos procesos secundarios que son servidores WSGI. Para terminarlos hacemos un GET en una URL especial; esto hace que el niño limpie y salga.
En * nix, tal vez el uso de grupos de procesos puede ayudarlo; también puede detectar subprocesos generados por sus subprocesos.
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
Otra consideración es escalar las señales: de SIGTERM (señal predeterminada para kill
) a SIGKILL (también conocido como kill -9
). Espere un momento entre las señales para darle al proceso la oportunidad de salir limpiamente antes de kill -9
.
Esto es lo que hice para mi aplicación posix:
Cuando existe su aplicación, llame al método kill () de esta clase: http://www.pixelbeat.org/libs/subProcess.py
Ejemplo de uso aquí: http://code.google.com/p/fslint/source/browse/trunk/fslint-gui#608
ayuda para el código python: http://docs.python.org/dev/library/subprocess.html#subprocess.Popen.wait
Necesitaba una pequeña variación de este problema (limpiar subprocesos, pero sin salir del programa Python mismo), y dado que no se menciona aquí entre las otras respuestas:
p=subprocess.Popen(your_command, preexec_fn=os.setsid)
os.killpg(os.getpgid(p.pid), 15)
setsid
ejecutará el programa en una nueva sesión, asignándole así un nuevo grupo de proceso y sus hijos. llamar a os.killpg
en él no reducirá su propio proceso de python también.
Advertencia: ¡solo para Linux! Puede hacer que su hijo reciba una señal cuando su padre fallezca.
Primero instale python-prctl == 1.5.0 luego cambie su código padre para iniciar sus procesos hijo de la siguiente manera
subprocess.Popen(["sleep", "100"], preexec_fn=lambda: prctl.set_pdeathsig(signal.SIGKILL))
Lo que esto dice es:
- lanzamiento del subproceso: dormir 100
- después del bifurcación y antes del ejecutivo del subproceso, el niño se registra para "enviarme un SIGKILL cuando mi padre termina".
La respuesta de Orip es útil, pero tiene la desventaja de que mata su proceso y devuelve un código de error a su padre. Lo evité así:
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
Y entonces:
with CleanChildProcesses():
# Do your work here
Por supuesto, puede hacer esto con try / except / finally, pero debe manejar los casos excepcionales y no excepcionales por separado.
Puede usar atexit para esto y registrar las tareas de limpieza que se ejecutarán cuando finalice su programa.
atexit.register (func [, * args [, ** kargs]])
En el proceso de limpieza, también puede implementar su propia espera y eliminarla cuando se produce el tiempo de espera deseado.
>>> 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!
Nota : las funciones registradas no se ejecutarán si este proceso (proceso principal) se cancela.
El siguiente método de Windows ya no es necesario para python> = 2.6
Aquí hay una manera de matar un proceso en Windows. Su objeto Popen tiene un atributo pid, por lo que puede simplemente llamarlo mediante success = win_kill (p.pid) (Necesita pywin32 instalado):
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
De hecho, necesitaba hacer esto, pero implicaba ejecutar comandos remotos. Queríamos poder detener los procesos al cerrar la conexión al servidor. Además, si, por ejemplo, está ejecutando en python repl, puede seleccionar ejecutar en primer plano si desea poder usar Ctrl-C para salir.
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()
Gracias a Malcolm Handley por el diseño inicial. Hecho con python2.7 en Linux.
Encuentre una solución para Linux (sin instalar 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))
Una solución para Windows puede ser utilizar la aplicación de trabajo win32 por ejemplo, ¿cómo destruyo automáticamente los procesos hijos en Windows?
Aquí hay una implementación de python existente