getoutput español python linux windows subprocess kill

español - subprocess python windows



Python: ¿cómo matar los procesos infantiles cuando el padre muere? (2)

¡Heh, yo solo estaba investigando esto ayer! Asumiendo que no puedes alterar el programa infantil:

En Linux, prctl(PR_SET_PDEATHSIG, ...) es probablemente la única opción confiable. (Si es absolutamente necesario que se elimine el proceso secundario, es posible que desee establecer la señal de muerte en SIGKILL en lugar de SIGTERM; el código al que se vincula utiliza SIGTERM, pero el niño tiene la opción de ignorar SIGTERM si así lo desea. )

En Windows, las opciones más confiables es usar un objeto Job . La idea es que cree un "Trabajo" (un tipo de contenedor para procesos), luego coloque el proceso hijo en el Trabajo y configure la opción mágica que dice "cuando nadie tiene un ''identificador'' para este Trabajo. , luego mata los procesos que están en él ". De forma predeterminada, el único ''identificador'' del trabajo es el que mantiene su proceso principal, y cuando el proceso primario finaliza, el sistema operativo pasará y cerrará todos sus manejadores, y luego notará que esto significa que no hay manejadores abiertos para el trabajo. Así que entonces mata al niño, según lo solicitado. (Si tiene varios procesos secundarios, puede asignarlos todos al mismo trabajo). Esta respuesta tiene un código de ejemplo para hacerlo, usando el módulo win32api . Ese código utiliza CreateProcess para iniciar el hijo, en lugar de subprocess.Popen . El motivo es que necesitan obtener un "identificador de proceso" para el hijo generado, y CreateProcess devuelve de forma predeterminada. Si prefiere usar subprocess.Popen , entonces aquí hay una copia (no probada) del código de esa respuesta, que usa subprocess.Popen y OpenProcess lugar de CreateProcess :

import subprocess import win32api import win32con import win32job hJob = win32job.CreateJobObject(None, "") extended_info = win32job.QueryInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation) extended_info[''BasicLimitInformation''][''LimitFlags''] = win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE win32job.SetInformationJobObject(hJob, win32job.JobObjectExtendedLimitInformation, extended_info) child = subprocess.Popen(...) # Convert process id to process handle: perms = win32con.PROCESS_TERMINATE | win32con.PROCESS_SET_QUOTA hProcess = win32api.OpenProcess(perms, False, child.pid) win32job.AssignProcessToJobObject(hJob, hProcess)

Técnicamente, hay una pequeña condición de carrera aquí en caso de que el niño muera entre las llamadas de Popen y OpenProcess , puedes decidir si quieres preocuparte por eso.

Una desventaja de usar un objeto de trabajo es que cuando se ejecuta en Vista o Win7, si su programa se inicia desde el shell de Windows (es decir, al hacer clic en un icono), es probable que ya haya un objeto de trabajo asignado e intente crear un El nuevo objeto de trabajo fallará. Win8 soluciona esto (permitiendo que los objetos de trabajo estén anidados), o si su programa se ejecuta desde la línea de comandos, entonces debería estar bien.

Si puede modificar el hijo (por ejemplo, como cuando se usa el multiprocessing ), entonces probablemente la mejor opción es pasar el PID del padre al hijo (por ejemplo, como un argumento de línea de comando, o en el argumento args= a multiprocessing.Process ), y entonces:

En POSIX: Genere un hilo en el hijo que solo llame a os.getppid() vez en cuando, y si el valor de retorno deja de coincidir con el pid pasado del padre, llame a os._exit() . (Este enfoque es portátil para todos los Unixes, incluido OS X, mientras que el truco prctl es específico de Linux).

En Windows: Genere un hilo en el hijo que utiliza OpenProcess y os.waitpid . Ejemplo usando ctypes:

from ctypes import WinDLL, WinError from ctypes.wintypes import DWORD, BOOL, HANDLE # Magic value from http://msdn.microsoft.com/en-us/library/ms684880.aspx SYNCHRONIZE = 0x00100000 kernel32 = WinDLL("kernel32.dll") kernel32.OpenProcess.argtypes = (DWORD, BOOL, DWORD) kernel32.OpenProcess.restype = HANDLE parent_handle = kernel32.OpenProcess(SYNCHRONIZE, False, parent_pid) # Block until parent exits os.waitpid(parent_handle, 0) os._exit(0)

Esto evita cualquiera de los posibles problemas con los objetos de trabajo que mencioné.

Si desea estar realmente seguro, puede combinar todas estas soluciones.

¡Espero que ayude!

El proceso hijo se inicia con

subprocess.Popen(arg)

¿Hay alguna manera de asegurar que se elimine cuando el padre termina de forma anormal? Necesito esto para trabajar tanto en Windows como en Linux. Soy consciente de esta solución para Linux .

Editar:

el requisito de iniciar un proceso hijo con subprocess.Popen(arg) se puede relajar, si existe una solución utilizando un método diferente para iniciar un proceso.


El objeto Popen ofrece los métodos de terminar y matar.

https://docs.python.org/2/library/subprocess.html#subprocess.Popen.terminate

Estos envían las señales SIGTERM y SIGKILL para usted. Puedes hacer algo parecido a lo siguiente:

from subprocess import Popen p = None try: p = Popen(arg) # some code here except Exception as ex: print ''Parent program has exited with the below error:/n{0}''.format(ex) if p: p.terminate()

ACTUALIZAR:

Usted tiene razón: el código anterior no lo protegerá contra choques duros o alguien que mate su proceso. En ese caso, puede intentar ajustar el proceso secundario en una clase y emplear un modelo de sondeo para ver el proceso principal. Tenga en cuenta que psutil no es estándar.

import os import psutil from multiprocessing import Process from time import sleep class MyProcessAbstraction(object): def __init__(self, parent_pid, command): """ @type parent_pid: int @type command: str """ self._child = None self._cmd = command self._parent = psutil.Process(pid=parent_pid) def run_child(self): """ Start a child process by running self._cmd. Wait until the parent process (self._parent) has died, then kill the child. """ print ''---- Running command: "%s" ----'' % self._cmd self._child = psutil.Popen(self._cmd) try: while self._parent.status == psutil.STATUS_RUNNING: sleep(1) except psutil.NoSuchProcess: pass finally: print ''---- Terminating child PID %s ----'' % self._child.pid self._child.terminate() if __name__ == "__main__": parent = os.getpid() child = MyProcessAbstraction(parent, ''ping -t localhost'') child_proc = Process(target=child.run_child) child_proc.daemon = True child_proc.start() print ''---- Try killing PID: %s ----'' % parent while True: sleep(1)

En este ejemplo, ejecuto ''ping -t localhost'' b / c que se ejecutará para siempre. Si mata el proceso principal, el proceso secundario (el comando ping) también se eliminará.