example español ejemplo python subprocess signals

español - subprocess python 3 example



¿Cómo evitar que Python propague señales a subprocesos? (5)

Combinando algunas de las otras respuestas que harán el truco, ninguna señal enviada a la aplicación principal se enviará al subproceso.

import os from subprocess import Popen def preexec(): # Don''t forward signals. os.setpgrp() Popen(''whatever'', preexec_fn = preexec)

Estoy usando python para manejar algunas simulaciones. Construyo los parámetros y ejecuto el programa usando:

pipe = open(''/dev/null'', ''w'') pid = subprocess.Popen(shlex.split(command), stdout=pipe, stderr=pipe)

Mi código maneja diferentes señales. Ctrl + C detendrá la simulación, me preguntará si quiero guardar y saldrá correctamente. Tengo otros manejadores de señales (por ejemplo, para forzar la salida de datos).

Lo que quiero es enviar una señal (SIGINT, Ctrl + C) a mi script de python que le preguntará al usuario qué señal quiere enviar al programa.

Lo único que impide que el código funcione es que parece que, haga lo que haga, Ctrl + C se "reenviará" al subproceso: el código lo detectará y saldrá:

try: <wait for available slots> except KeyboardInterrupt: print "KeyboardInterrupt catched! All simulations are paused. Please choose the signal to send:" print " 0: SIGCONT (Continue simulation)" print " 1: SIGINT (Exit and save)" [...] answer = raw_input() pid.send_signal(signal.SIGCONT) if (answer == "0"): print " --> Continuing simulation..." elif (answer == "1"): print " --> Exit and save." pid.send_signal(signal.SIGINT) [...]

Entonces, haga lo que haga, el programa está recibiendo el SIGINT que solo quiero que vea mi script de python. ¿¿¿Cómo puedo hacer eso???

También intenté:

signal.signal(signal.SIGINT, signal.SIG_IGN) pid = subprocess.Popen(shlex.split(command), stdout=pipe, stderr=pipe) signal.signal(signal.SIGINT, signal.SIG_DFL)

para ejecutar el programa, pero esto da el mismo resultado: el programa atrapa el SIGINT.

Gracias!


Esto puede hacerse usando ctypes . Realmente no recomendaría esta solución, pero me interesaba lo suficiente como para cocinar algo, así que pensé en compartirla.

parent.py

#!/usr/bin/python from ctypes import * import signal import subprocess import sys import time # Get the size of the array used to # represent the signal mask SIGSET_NWORDS = 1024 / (8 * sizeof(c_ulong)) # Define the sigset_t structure class SIGSET(Structure): _fields_ = [ (''val'', c_ulong * SIGSET_NWORDS) ] # Create a new sigset_t to mask out SIGINT sigs = (c_ulong * SIGSET_NWORDS)() sigs[0] = 2 ** (signal.SIGINT - 1) mask = SIGSET(sigs) libc = CDLL(''libc.so.6'') def handle(sig, _): if sig == signal.SIGINT: print("SIGINT from parent!") def disable_sig(): ''''''Mask the SIGINT in the child process'''''' SIG_BLOCK = 0 libc.sigprocmask(SIG_BLOCK, pointer(mask), 0) # Set up the parent''s signal handler signal.signal(signal.SIGINT, handle) # Call the child process pid = subprocess.Popen("./child.py", stdout=sys.stdout, stderr=sys.stdin, preexec_fn=disable_sig) while (1): time.sleep(1)

niño.py

#!/usr/bin/python import time import signal def handle(sig, _): if sig == signal.SIGINT: print("SIGINT from child!") signal.signal(signal.SIGINT, handle) while (1): time.sleep(1)

Tenga en cuenta que esto hace un montón de suposiciones sobre varias estructuras libc y, como tal, es probablemente bastante frágil. Cuando corres, no verás el mensaje "SIGINT from child!" impreso. Sin embargo, si sigprocmask la llamada a sigprocmask , lo harás. Parece hacer el trabajo :)


La función:

os.setpgrp()

Funciona bien solo si se llama a Popen justo después. Si está intentando evitar que las señales se propaguen a los subprocesos de un paquete arbitrario, entonces el paquete puede anular esto antes de crear subprocesos que hagan que las señales se propagen de todos modos. Este es el caso cuando, por ejemplo, se intenta evitar la propagación de la señal en los procesos del navegador web generados a partir del paquete Selenium.

Esta función también elimina la capacidad de comunicarse fácilmente entre los procesos separados sin algo como sockets.

Para mis propósitos, esto parecía una exageración. En lugar de preocuparme por la propagación de señales, usé la señal personalizada SIGUSR1. Muchos paquetes de Python ignoran SIGUSR1, por lo que incluso si se envía a todos los subprocesos, generalmente se ignorará

Se puede enviar a un proceso en bash en Ubuntu usando

kill -10 <pid>

Puede ser reconocido en su código a través de

signal.signal(signal.SIGUSR1, callback_function)

Los números de señal disponibles en Ubuntu se pueden encontrar en /usr/include/asm/signal.h .


POSIX dice que un programa ejecutado con execvp (que es lo que utiliza subprocess.Popen) debe heredar la máscara de señal del proceso de llamada.

Podría estar equivocado, pero no creo que la signal llamada modifique la máscara. Desea sigprocmask , que Python no expone directamente.

Sería un truco, pero podría intentar configurarlo a través de una llamada directa a libc a través de ctypes . Definitivamente estaría interesado en ver una mejor respuesta sobre esta estrategia.

La estrategia alternativa sería encuestar la entrada estándar del usuario como parte de su ciclo principal. ("Presione Q para salir / pausar", algo así.) Esto evita el problema de manejar las señales.


Resolví este problema creando una aplicación de ayuda a la que llamo en lugar de crear al niño directamente. Este ayudante cambia su grupo principal y luego genera el proceso hijo real.

import os import sys from time import sleep from subprocess import Popen POLL_INTERVAL=2 # dettach from parent group (no more inherited signals!) os.setpgrp() app = Popen(sys.argv[1:]) while app.poll() is None: sleep(POLL_INTERVAL) exit(app.returncode)

Llamo a este ayudante en el padre, pasando el niño real y sus parámetros como argumentos:

Popen(["helper", "child", "arg1", ...])

Tengo que hacer esto porque la aplicación de mi hijo no está bajo mi control, si lo hubiera hecho podría haber agregado el setpgrp allí y haber evitado la ayuda por completo.