Por que os processos gerados por Cron acabam extremos?
-
19-09-2019 - |
Pergunta
Eu tenho alguns processos aparecendo como <defunct>
dentro top
(e ps
). Eu fervei as coisas dos scripts e programas reais.
No meu crontab
:
* * * * * /tmp/launcher.sh /tmp/tester.sh
O conteúdo de launcher.sh
(que é claro que é executável marcado):
#!/bin/bash
# the real script does a little argument processing here
"$@"
O conteúdo de tester.sh
(que é claro que é executável marcado):
#!/bin/bash
sleep 27 & # the real script launches a compiled C program in the background
ps
mostra o seguinte:
user 24257 24256 0 18:32 ? 00:00:00 [launcher.sh] <defunct>
user 24259 1 0 18:32 ? 00:00:00 sleep 27
Observe que tester.sh
Não aparece-saiu após o lançamento do trabalho em segundo plano.
Porque launcher.sh
Fique por perto, marcado <defunct>
? Parece apenas fazer isso quando lançado por cron
-Não quando eu mesmo corro.
Nota adicional: launcher.sh
é um script comum no sistema em que isso é executado, que não é facilmente modificado. As outras coisas (crontab
, tester.sh
, até o programa que eu corro em vez de sleep
) pode ser modiificado com muito mais facilidade.
Solução
Porque eles não foram objeto de um wait(2)
Chamada do sistema.
Como alguém pode esperar por esses processos no futuro, o kernel não pode se livrar completamente deles ou não será capaz de executar o wait
Chamada do sistema porque não terá mais o status de saída ou evidência de sua existência.
Quando você inicia um da concha, seu shell está prendendo o Sigchld e fazendo várias operações de espera de qualquer maneira, então nada permanece extinto por muito tempo.
Mas Cron não está em um estado de espera, está dormindo, então a criança extinta pode ficar por um tempo até que Cron acorde.
Atualizar: Respondendo ao comentário ... hmm. Eu consegui duplicar o problema:
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
Então, o que aconteceu foi, eu acho:
- Cron Forks e Cron Child inicia a concha
- Shell (1636) inicia Sid e PGID 1636 e começa a dormir
- Shell sai, Sigchld enviado para Cron 3562
- o sinal é ignorado ou maltratado
- Shell se vira zumbi. Observe que o sono é reparado em iniciar, portanto, quando o sono sai, o Init receberá o sinal e a limpeza. Ainda estou tentando descobrir quando o zumbi é colhido. Provavelmente, sem crianças ativas, o Cron 1629 descobre que pode sair, nesse ponto o zumbi será reparado em iniciar e ser colhido. Então agora nos perguntamos sobre o Sigchld desaparecido que Cron deveria ter processado.
- Não é necessariamente culpa de Vixie Cron. Como você pode ver aqui, Libdaememon instala um manipulador de Sigchld durante
daemon_fork()
, e isso pode interferir na entrega de sinal em uma saída rápida pelo intermediário 1629Agora, eu nem sei se Vixie Cron no meu sistema Ubuntu é construído com libdaemon, mas pelo menos eu tenho uma nova teoria. :-)
- Não é necessariamente culpa de Vixie Cron. Como você pode ver aqui, Libdaememon instala um manipulador de Sigchld durante
Outras dicas
Suspeito que Cron esteja aguardando todos os subprocessos da sessão para terminar. Veja Wait (2) em relação a argumentos negativos da PID. Você pode ver a sess com:
ps faxo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
Aqui está o que vejo (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
Observe que o SH e o sono estão na mesma sess.
Use o comando Setsid (1). Aqui está tester.sh:
#!/bin/bash
setsid sleep 27 # the real script launches a compiled C program in the background
Observe que você não precisa &
, Setsid o coloca em segundo plano.
Na minha opinião, isso é causado pelo processo Crond (gerado por Crond para todas as tarefas) aguardando a contribuição no stdin, que é canalizada para o stdout/stderr do comando no crontab. Isso é feito porque o Cron é capaz de enviar a saída resultante por correio para o usuário.
Portanto, Crond está esperando o EOF até o comando do usuário e tudo o que seus processos infantis gerados fecharam o tubo. Se isso for feito, Crond continuar com a estatura de espera e, em seguida, o comando do usuário extinto desaparece.
Então, acho que você precisa desconectar explicitamente todos os subprocessos gerados no seu script do tubo (por exemplo, redirecionando -o para um arquivo ou /dev /null.
Portanto, a linha a seguir deve funcionar em Crontab:
* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & )
Eu recomendo que você resolva o problema simplesmente não tendo dois processos separados: tenha launcher.sh
Faça isso em sua última linha:
exec "$@"
Isso eliminará o processo supérfluo.
Encontrei essa pergunta enquanto procurava uma solução com um problema semelhante. Infelizmente, as respostas nesta pergunta não resolveram meu problema.
Matar o processo extinto não é uma opção, pois você precisa encontrar e matar seu processo pai. Acabei matando os processos extintos da seguinte maneira:
ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh
Em "Grep" ", você pode restringir a pesquisa a um processo extinto específico que procura.
Eu testei o mesmo problema tantas vezes. E finalmente eu tenho a solução. Basta especificar o '/bin/bash' antes do script bash, como mostrado abaixo.
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh