2009-10-01 20 views
13

Tengo algunos procesos que aparecen como <defunct> en top (y ps). He reducido las cosas de los scripts y programas reales.¿Por qué los procesos generados por cron acaban sin funcionar?

En mis crontab:

* * * * * /tmp/launcher.sh /tmp/tester.sh 

El contenido de launcher.sh (que es, por supuesto, marcado como ejecutable):

#!/bin/bash 
# the real script does a little argument processing here 
"[email protected]" 

El contenido de tester.sh (que es, por supuesto, marcada ejecutables):

#!/bin/bash 
sleep 27 & # the real script launches a compiled C program in the background 

ps muestra lo siguiente:

user  24257 24256 0 18:32 ?  00:00:00 [launcher.sh] <defunct> 
user  24259  1 0 18:32 ?  00:00:00 sleep 27 

Tenga en cuenta que tester.sh no parece - que ha salido después de lanzar el trabajo en segundo plano.

¿Por qué launcher.sh se quedan, marcados <defunct>? Solo parece hacer esto cuando se inicia por cron --no cuando lo ejecuto yo mismo.

Nota adicional: launcher.sh es una secuencia de comandos común en el sistema en el que se ejecuta, que no se modifica fácilmente. Las otras cosas (crontab, tester.sh, incluso el programa que ejecuto en lugar de sleep) se pueden modificar mucho más fácilmente.

+1

Por cierto, los procesos que marcó "' '" se llaman "zombies". – Teddy

+3

Una solución posible se dan en este hilo: http://stackoverflow.com/questions/3748432/insane-crond-behavior-keeps-making-defunct-bash-processes –

Respuesta

10

Porque no han sido objeto de una llamada al sistema wait(2).

Dado que alguien puede esperar estos procesos en el futuro, el kernel no puede deshacerse completamente de ellos o no podrá ejecutar la llamada al sistema wait porque no tendrá el estado de salida o evidencia de su existencia más.

Cuando inicia uno desde el shell, su caparazón está atrapando SIGCHLD y haciendo varias operaciones de espera de todos modos, por lo que nada permanece difunto por mucho tiempo.

Pero cron no está en estado de espera, está durmiendo, por lo que el niño difunto puede quedarse un rato hasta que cron se despierte.


Actualización:   respuesta a comentar ... Hmm. Me las arreglé para duplicar el número:

PPID PID PGID SESS COMMAND 
    1 3562 3562 3562 cron 
3562 1629 3562 3562 \_ cron 
1629 1636 1636 1636  \_ sh <defunct> 
    1 1639 1636 1636 sleep 

Por lo tanto, lo que ocurrió fue, pienso:

  • horquillas cron y el niño cron empieza cáscara
  • cáscara (1636) comienza sid y pgid 1636 y arranques de sueño
  • shell sale, SIGCHLD envió a cron 3562
  • señal es ignorada o mal manejado
  • cáscara turnos zombi. Tenga en cuenta que sleep se vuelve a iniciar para init, por lo que cuando el sueño sale de init obtendrá la señal y la limpieza.Todavía estoy tratando de averiguar cuándo se cosecha el zombi. Probablemente sin niños activos, cron 1629 cree que puede salir, en ese punto el zombi se volverá a establecer para iniciarse y ser cosechado. Entonces ahora nos preguntamos sobre el SIGCHLD que falta que cron debería haber procesado.
    • No es necesariamente la falla de vixie cron. Como puede ver aquí, libdaemon installs a SIGCHLD handler durante daemon_fork(), y esto podría interferir con la entrega de señal en una salida rápida por intermedio de 1629

      Ahora, ni siquiera sé si vixie cron en mi sistema Ubuntu incluso está construido con libdaemon, pero al menos yo tener una nueva teoría :-)

+0

En realidad, se quedará todo el día, no solo hasta que cron se despierta. ¿Puedes comentar sobre eso? El programa real que ejecuto (no duermo) se ejecuta durante horas y horas. –

+2

..y ¿hay una solución adecuada para esto? ¿Puede el script hacer algo para asegurarse de que no se convierta en zombie cuando termine? – Superole

+0

Hola, ¿puedes decirme cómo reproducir este problema? –

3

te recomiendo que resolver el problema simplemente no tener dos procesos separados: Tener launcher.sh hacer esto en su última línea:

exec "[email protected]" 

Esto eliminará la superflua proceso.

+0

Creo que tienes razón, pero no puedo hacerlo fácilmente porque 'launcher.sh' es usado por muchas cosas, algunas de las cuales se romperían si hiciera este cambio. Yo * podría * considerar hacer un nuevo script de inicio que ejecute el comando exec y dejar intacta la otra versión, pero esto es bastante desagradable. –

+0

@John Zwinck: No me puedo imaginar en qué circunstancias las cosas se romperían si hicieras este cambio. Es efectivamente lo mismo con un proceso menos. – Teddy

+0

@Teddy: lo que se rompería es que algunas personas hacen esto en un shell interactivo: '. launcher.sh foo bar' Si el iniciador hacía 'exec', el shell del usuario terminaría una vez completado el programa lanzado. Sé que es un caso de uso extraño, pero así es en el sistema existente. –

3

Sospecho que cron está esperando a que terminen todos los subprocesos en la sesión. Ver wait (2) con respecto a los argumentos pid negativos. Se puede ver la SESS con:

ps faxo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm 

Aquí es lo que veo (editado):

STAT EUID RUID TT  TPGID SESS PGRP PPID PID %CPU COMMAND 
Ss  0  0 ?   -1 3197 3197  1 3197 0.0 cron 
S  0  0 ?   -1 3197 3197 3197 18825 0.0 \_ cron 
Zs 1000 1000 ?   -1 18832 18832 18825 18832 0.0  \_ sh <defunct> 
S  1000 1000 ?   -1 18832 18832  1 18836 0.0 sleep 

en cuenta que el pescado y el sueño son de la misma Ses.

Utilice el comando setsid (1). Esto es tester.sh:

#!/bin/bash 
setsid sleep 27 # the real script launches a compiled C program in the background 

Aviso que no es necesario &, setsid pone en el fondo.

+0

Hacer esto hace que 'launcher.sh' y' tester.sh' se queden. Me gustaría que ambos finalicen (al menos en mi situación original, 'tester.sh' termina; con' setsid' no lo hace, lo que no quiero). –

+0

Es extraño, tanto el iniciador como el probador terminan cuando lo ejecuto aquí. (Casi de inmediato: todavía tengo que tomar una instantánea ps donde los veo en ejecución.) – bstpierre

+0

Estoy usando Ubuntu Hardy de 64 bits. ¿Que pasa contigo? –

0

Encontré esta pregunta mientras buscaba una solución con un problema similar. Desafortunadamente las respuestas en esta pregunta no resolvieron mi problema.

El proceso de extinción de la muerte no es una opción, ya que es necesario encontrar y eliminar el proceso principal. Terminé matar a los procesos inactivos de la siguiente manera:

ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh 

En "grep ''" se puede limitar la búsqueda a un proceso desaparecida específica que está después.

-2

He probado el mismo problema tantas veces. Y finalmente tengo la solución. Simplemente especifique el '/ bin/bash' antes del script bash como se muestra a continuación.

 
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh 
2

a mi opinión es causada por crond proceso (generada por crond para cada tarea) a la espera de la entrada en la entrada estándar que se canaliza al stdout/stderr del comando en el crontab. Esto se hace porque cron puede enviar la salida resultante por correo al usuario.

Así que CROND está esperando EOF hasta que el comando del usuario y todos sus procesos hijo generados hayan cerrado la tubería. Si esto se hace, CROND continúa con la declaración de espera y luego desaparece el comando de usuario desaparecido.

Así que creo que debe desconectar explícitamente cada subproceso generado en su secuencia de comandos de la tubería (por ejemplo, redirigiéndolo a un archivo o/dev/null.

por lo que la siguiente línea debe trabajar en el crontab:

* * * * * (/tmp/launcher.sh /tmp/tester.sh &>/dev/null &) 
+0

¡Esto funciona y tiene sentido! –

Cuestiones relacionadas