Почему процессы, порожденные cron, перестают функционировать? - PullRequest
14 голосов
/ 02 октября 2009

У меня есть некоторые процессы, которые отображаются как <defunct> в topps). Я свел вещи из настоящих скриптов и программ.

По моему crontab:

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

Содержимое launcher.sh (которое, конечно, помечено как исполняемый):

#!/bin/bash
# the real script does a little argument processing here
"$@"

Содержимое tester.sh (которое, конечно, помечено как исполняемый):

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

ps показывает следующее:

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

Обратите внимание, что tester.sh не отображается - оно завершилось после запуска фонового задания.

Почему launcher.sh остается рядом, помечено <defunct>? Кажется, он делает это только при запуске cron, а не когда запускаю его сам.

Дополнительное примечание: launcher.sh - это распространенный скрипт в системе, на котором он выполняется, который нелегко изменить. Другие вещи (crontab, tester.sh, даже программа, которую я запускаю вместо sleep) могут быть изменены намного легче.

Ответы [ 6 ]

11 голосов
/ 02 октября 2009

Поскольку они не были предметом системного вызова wait(2).

Поскольку кто-то может ждать этих процессов в будущем, ядро ​​не сможет полностью избавиться от них или не сможет выполнить системный вызов wait, поскольку у него не будет состояния выхода или подтверждения его существования больше.

Когда вы запускаете один из командной оболочки, ваша оболочка перехватывает SIGCHLD и в любом случае выполняет различные операции ожидания, поэтому ничто не может оставаться недействительным надолго.

Но cron не находится в состоянии ожидания, он спит, поэтому несуществующий ребенок может какое-то время задерживаться, пока cron не проснется.


Обновление: Ответ на комментарий ... Хм. Мне удалось продублировать проблему:

 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

Итак, что случилось, я думаю:

  • cron forks и cron child запускают оболочку
  • shell (1636) запускает sid и pgid 1636 и запускает сон
  • оболочка выходит, SIGCHLD отправлено cron 3562
  • сигнал игнорируется или неправильно обрабатывается
  • оболочка превращается в зомби. Обратите внимание, что сон перезапускается в init, поэтому при выходе из сна init получит сигнал и очистится. Я все еще пытаюсь понять, когда зомби пожнут. Вероятно, не имея активных детей, Cron 1629 узнает, что он может выйти, и в этот момент зомби будет перерожден для инициации и получен. Так что теперь мы задаемся вопросом об отсутствующем SIGCHLD, который должен был обработать cron.
    • Это не обязательно вина Винси Крона. Как вы можете видеть здесь, libdaemon устанавливает обработчик SIGCHLD во время daemon_fork(), и это может помешать доставке сигнала при быстром выходе на промежуточном уровне 1629

      Теперь я даже не знаю, vixie cron в моей системе Ubuntu даже собрана библиотека libdaemon, но по крайней мере у меня есть новая теория. :-)

4 голосов
/ 25 марта 2014

По моему мнению, это вызвано процессом CROND (порожденным crond для каждой задачи), ожидающим ввода в stdin, который передается в stdout / stderr команды в crontab. Это сделано потому, что cron может отправлять полученные результаты по почте пользователю.

Таким образом, CROND ожидает EOF до тех пор, пока пользовательская команда и все ее порожденные дочерние процессы не закроют канал. Если это сделано, CROND продолжает выполнение инструкции wait, а затем команда defunct user исчезает.

Так что я думаю, что вы должны явно отключить каждый порожденный подпроцесс в вашем скрипте от канала (например, перенаправив его в файл или /dev/null.

поэтому следующая строка должна работать в crontab:

* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & ) 
4 голосов
/ 02 октября 2009

Я подозреваю, что cron ожидает завершения всех подпроцессов в сеансе. Смотрите wait (2) относительно отрицательных аргументов pid. Вы можете увидеть SESS с:

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

Вот что я вижу (отредактировано):

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

Обратите внимание, что sh и сон находятся в одной и той же SESS.

Используйте команду setsid (1). Вот тестер.sh:

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

Обратите внимание, что вам не нужно &, setsid ставит его в фоновом режиме.

3 голосов
/ 02 октября 2009

Я бы порекомендовал вам решить проблему, просто не имея двух отдельных процессов: launcher.sh сделайте это в последней строке:

exec "$@"

Это устранит лишний процесс.

1 голос
/ 22 октября 2011

Я нашел этот вопрос, когда искал решение с похожей проблемой. К сожалению, ответы на этот вопрос не решили мою проблему.

Уничтожение несуществующего процесса не вариант, так как вам нужно найти и уничтожить его родительский процесс. Я закончил тем, что убил несуществующие процессы следующим образом:

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

В "grep ''" вы можете сузить поиск до определенного несуществующего процесса, который вы выполняете.

0 голосов
/ 21 марта 2012

Я много раз проверял одну и ту же проблему. И наконец у меня есть решение. Просто укажите '/ bin / bash' перед сценарием bash, как показано ниже.

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