español - subprocess python 3 example
cómo matar(o evitar) procesos zombie con módulo de subproceso (8)
El tiempo de ejecución de python asume la responsabilidad de deshacerse del proceso zombie una vez que sus objetos de proceso han sido recogidos en la basura. Si ve al zombie tirado alrededor, significa que ha mantenido un objeto de proceso y no ha llamado a esperar, sondear o terminar en él.
Cuando inicio un script de python desde otro script de python utilizando el módulo de subproceso, se crea un proceso zombie cuando el subproceso se "completa". No puedo matar a este subproceso a menos que mate el proceso de python de mi padre.
¿Hay una manera de matar al subproceso sin matar al padre? Sé que puedo hacer esto utilizando wait (), pero necesito ejecutar mi script con no_wait ().
No estoy completamente seguro de lo que quieres decir con no_wait()
. ¿Quiere decir que no puede bloquear la espera de que finalicen los procesos secundarios? Suponiendo que sí, creo que esto hará lo que quieras:
os.wait3(os.WNOHANG)
No estoy seguro de lo que quiere decir "Necesito ejecutar mi script con no_wait ()", pero creo que este ejemplo hace lo que necesita. Los procesos no serán zombies por mucho tiempo. El proceso principal solo wait()
en ellos cuando ya hayan finalizado y, por lo tanto, se descompondrán rápidamente.
#!/usr/bin/env python2.6
import subprocess
import sys
import time
children = []
#Step 1: Launch all the children asynchronously
for i in range(10):
#For testing, launch a subshell that will sleep various times
popen = subprocess.Popen(["/bin/sh", "-c", "sleep %s" % (i + 8)])
children.append(popen)
print "launched subprocess PID %s" % popen.pid
#reverse the list just to prove we wait on children in the order they finish,
#not necessarily the order they start
children.reverse()
#Step 2: loop until all children are terminated
while children:
#Step 3: poll all active children in order
children[:] = [child for child in children if child.poll() is None]
print "Still running: %s" % [popen.pid for popen in children]
time.sleep(1)
print "All children terminated"
La salida hacia el final se ve así:
Still running: [29776, 29774, 29772]
Still running: [29776, 29774]
Still running: [29776]
Still running: []
All children terminated
Recientemente, me encontré con este problema de zombies debido a mi script en Python. El problema real se debió principalmente a la muerte del subproceso y el proceso de los padres no sabe que el niño está muerto. Entonces, lo que hice fue simplemente agregar popen.communicate () después de la señal de muerte del proceso hijo para que el proceso padre sepa que el niño está muerto, luego el kernel actualiza el pid del childprocess ya que el niño ya no está y Así que no hay zombies formados ahora.
PS: poll también es una opción aquí, ya que verifica y transmite el estado secundario al padre. A menudo, en el subproceso es mejor si usas check_output o call si no necesitas comunicarte con stdout y stdin.
Si elimina el objeto de subproceso, usando del para forzar la recolección de basura, eso hará que se elimine el objeto de subproceso y luego los procesos inactivos desaparecerán sin terminar su intérprete. Puede probar esto primero en la interfaz de línea de comandos de python.
Si simplemente utiliza subprocess.Popen
, estará bien. He aquí cómo:
import subprocess
def spawn_some_children():
subprocess.Popen(["sleep", "3"])
subprocess.Popen(["sleep", "3"])
subprocess.Popen(["sleep", "3"])
def do_some_stuff():
spawn_some_children()
# do some stuff
print "children went out to play, now I can do my job..."
# do more stuff
if __name__ == ''__main__'':
do_some_stuff()
Puede usar .poll()
en el objeto devuelto por Popen para verificar si terminó (sin esperar). Si devuelve None
, el niño todavía se está ejecutando.
Asegúrate de no mantener las referencias a los objetos Popen. Si lo haces, no serán recolectados en la basura, por lo que terminas con zombies. Aquí hay un ejemplo:
import subprocess
def spawn_some_children():
children = []
children.append(subprocess.Popen(["sleep", "3"]))
children.append(subprocess.Popen(["sleep", "3"]))
children.append(subprocess.Popen(["sleep", "3"]))
return children
def do_some_stuff():
children = spawn_some_children()
# do some stuff
print "children went out to play, now I can do my job..."
# do more stuff
# if children finish while we are in this function,
# they will become zombies - because we keep a reference to them
En el ejemplo anterior, si desea deshacerse de los zombies, puede .wait()
en cada uno de los niños o .poll()
hasta que el resultado no sea None
.
De cualquier manera está bien, ya sea no guardar referencias o usar .wait()
o .poll()
.
Un proceso zombie no es un proceso real; es solo una entrada restante en la tabla de procesos hasta que el proceso primario solicite el código de retorno del menor. El proceso real ha finalizado y no requiere otros recursos sino dicha entrada de la tabla de procesos.
Probablemente necesitemos más información sobre los procesos que ejecuta para poder ayudar más.
Sin embargo, en el caso de que su programa Python sepa cuándo han finalizado los procesos secundarios (por ejemplo, al alcanzar el final de los datos de la salida estándar del niño), puede llamar a process.wait()
forma segura process.wait()
import subprocess
process= subprocess.Popen( (''ls'', ''-l'', ''/tmp''), stdout=subprocess.PIPE)
for line in process.stdout:
pass
subprocess.call( (''ps'', ''-l'') )
process.wait()
print "after wait"
subprocess.call( (''ps'', ''-l'') )
Ejemplo de salida:
$ python so2760652.py
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 501 21328 21326 0 80 0 - 1574 wait pts/2 00:00:00 bash
0 S 501 21516 21328 0 80 0 - 1434 wait pts/2 00:00:00 python
0 Z 501 21517 21516 0 80 0 - 0 exit pts/2 00:00:00 ls <defunct>
0 R 501 21518 21516 0 80 0 - 608 - pts/2 00:00:00 ps
after wait
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD
0 S 501 21328 21326 0 80 0 - 1574 wait pts/2 00:00:00 bash
0 S 501 21516 21328 0 80 0 - 1467 wait pts/2 00:00:00 python
0 R 501 21519 21516 0 80 0 - 608 - pts/2 00:00:00 ps
De lo contrario, puede mantener a todos los niños en una lista, y de vez en cuando .poll
para sus códigos de devolución. Después de cada iteración, recuerde eliminar de la lista a los hijos con códigos de retorno diferentes a None
(es decir, los terminados).
No usar Popen.communicate()
o call()
resultará en un proceso zombie.
Si no necesita la salida del comando, puede usar subprocess.call()
:
>>> import subprocess
>>> subprocess.call([''grep'', ''jdoe'', ''/etc/passwd''])
0
Si la salida es importante, debe usar Popen()
y se communicate()
para obtener la salida Popen()
y stderr.
>>> from subprocess import Popen, PIPE
>>> process = Popen([''ls'', ''-l'', ''/tmp''], stdout=PIPE, stderr=PIPE)
>>> stdout, stderr = process.communicate()
>>> stderr
''''
>>> print stdout
total 0
-rw-r--r-- 1 jdoe jdoe 0 2010-05-03 17:05 bar
-rw-r--r-- 1 jdoe jdoe 0 2010-05-03 17:05 baz
-rw-r--r-- 1 jdoe jdoe 0 2010-05-03 17:05 foo