example español python linux subprocess

español - ¿Cómo matar un proceso hijo de Python creado con subprocess.check_output() cuando el padre muere?



subprocess python 3 example (4)

Manualmente puedes hacer esto:

ps aux | grep <process name>

obtener el PID (segunda columna) y

kill -9 <PID> -9 es forzar matarlo

Estoy ejecutando en una máquina Linux un script de python que crea un proceso secundario usando subprocess.check_output () como sigue:

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

El problema es que incluso si el proceso padre muere, el niño todavía se está ejecutando. ¿Hay alguna manera de que pueda matar el proceso hijo cuando el padre muere?


No sé los detalles, pero la mejor manera es capturar errores (y quizás incluso todos los errores) con señal y terminar cualquier proceso restante allí.

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

Esto es solo una maqueta, necesitarías captar algo más que SIGINT, pero la idea podría ayudarte a comenzar y deberías verificar el proceso generado de alguna manera.

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

Recomiendo volver a escribir una versión personalizada de la causa check_output ya que me di cuenta de que check_output es realmente solo para una depuración simple, etc., ya que no puedes interactuar tanto con ella durante la ejecución.

Reescribir 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

Y haga lo que quiera con él, tal vez almacene las ejecuciones activas en una variable temporal y elimínelas al salir con una señal u otro medio de intecepción de errores / paradas del bucle principal.

Al final, aún debe capturar las terminaciones en la aplicación principal para matar de forma segura a cualquier niño, la mejor manera de abordar esto es con try & except o signal .


Sí, puedes lograr esto por dos métodos. Ambos requieren que uses Popen lugar de check_output . El primero es un método más simple, usando try..finalmente, como sigue:

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

Esto capturará sigint (interrupción del teclado) y sigterm, pero no sigkill (si matas tu script con -9).

El otro método es un poco más complejo y usa ctypes ''prctl PR_SET_PDEATHSIG. El sistema enviará una señal al niño una vez que el padre salga por cualquier motivo (incluso 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))


Su problema es con el uso de subprocess.check_output : está en lo cierto, no puede obtener el PID secundario usando esa interfaz. Use Popen en su lugar:

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

Para asegurarse de matar al niño al salir:

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)