Почему процессы, порожденные cron, заканчиваются несуществующими?

у меня есть некоторые процессы отображаются как <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 не появляется--it вышел после запуска фонового задания.

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

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

6 ответов


потому что они не были предметом wait(2) системный вызов.

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

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

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


обновление: отвечая на замечание... Хм. Мне удалось дублировать проблему:

 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 вилки и cron ребенок начинает shell
  • shell (1636) запускает sid и pgid 1636 и начинает спать
  • оболочка выходит, SIGCHLD отправлено в cron 3562
  • сигнал игнорируется или неправильно обрабатывается
  • shell превращает зомби. Обратите внимание, что сон возвращается в init, поэтому, когда сон выходит из init, он получит сигнал и очистится. Я все еще пытаюсь понять, когда зомби пожнут. Вероятно, без активных детей cron 1629 выясняет, что он может выйти, в этот момент зомби будет переродиться в init и получить жатву. Итак, теперь мы задаемся вопросом о пропавшем SIGCHLD, который должен был иметь cron обработанный.
    • это не обязательно вина Викси крон. Как вы можете видеть здесь, libdaemon устанавливает обработчик SIGCHLD во время daemon_fork(), и это может помешать доставке сигнала на быстром выходе промежуточным 1629

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


Я подозреваю, что 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

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

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

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


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

exec "$@"

Это исключит излишний процесс.


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

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

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

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

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

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

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

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

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


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

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