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

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

In my 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) можно изменить гораздо проще.


person John Zwinck    schedule 01.10.2009    source источник
comment
Кстати, процессы с пометкой <defunct> называются зомби.   -  person Teddy    schedule 02.10.2009
comment
Возможное решение приведено в этой теме: stackoverflow.com/questions/3748432/   -  person    schedule 02.12.2011


Ответы (6)


Потому что они не были предметом системного вызова 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-потомок запускает оболочку
  • оболочка (1636) запускает sid и pgid 1636 и запускает сон
  • оболочка завершает работу, SIGCHLD отправляется в cron 3562
  • сигнал игнорируется или неправильно обрабатывается
  • shell turns zombie. Note that sleep is reparented to init, so when the sleep exits init will get the signal and clean up. I'm still trying to figure out when the zombie gets reaped. Probably with no active children cron 1629 figures out it can exit, at that point the zombie will be reparented to init and get reaped. So now we wonder about the missing SIGCHLD that cron should have processed.
    • It isn't necessarily vixie cron's fault. As you can see here, libdaemon installs a SIGCHLD handler during daemon_fork(), and this could interfere with signal delivery on a quick exit by intermediate 1629

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

person DigitalRoss    schedule 01.10.2009
comment
На самом деле он будет работать весь день, а не только до тех пор, пока cron не проснется. Вы можете это прокомментировать? Настоящая программа, которую я запускаю (не сплю), работает часами и часами. - person John Zwinck; 02.10.2009
comment
..и есть ли правильное решение для этого? может ли скрипт сделать что-нибудь, чтобы убедиться, что он не превратится в зомби, когда он закончит? - person Superole; 19.04.2013
comment
Здравствуйте, подскажите как воспроизвести эту проблему? - person Wenlin.Wu; 11.02.2018
comment
Может быть, команда, которая будет производить вывод, сделает cron зомби? Просто предположение. - person weaming; 21.02.2020

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

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

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

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

* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & ) 
person hp4    schedule 25.03.2014
comment
Спасибо, этот пост подарил мне счастье посреди ночи. - person Ricardo Cristian Ramirez; 06.07.2018

Я подозреваю, что 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 и sleep находятся в одном и том же SESS.

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

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

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

person bstpierre    schedule 01.10.2009
comment
Это приводит к тому, что launcher.sh и tester.sh остаются рядом. Я бы хотел, чтобы они оба завершились (по крайней мере, в моей исходной ситуации tester.sh завершается, а setsid нет, чего я не хочу). - person John Zwinck; 02.10.2009
comment
Это странно, и лаунчер, и тестер закрываются, когда я запускаю его здесь. (Почти сразу — мне еще предстоит сделать снимок PS, на котором я вижу, как они работают.) - person bstpierre; 02.10.2009
comment
Я использую 64-разрядную версию Ubuntu Hardy. А вы? - person John Zwinck; 02.10.2009
comment
О, и у меня есть SHELL=/bin/bash в верхней части моего crontab. - person John Zwinck; 02.10.2009
comment
Ubuntu jaunty 32. В моем crontab нет bash. cron 3.0pl1-105ubuntu1.1 - person bstpierre; 08.10.2009

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

exec "$@"

Это избавит от лишнего процесса.

person Teddy    schedule 01.10.2009
comment
Я думаю, вы правы, но я не могу легко сделать это, потому что launcher.sh используется многими вещами, некоторые из которых сломаются, если я внесу это изменение. Я мог бы рассмотреть возможность создания нового сценария запуска, который выполняет exec, и оставить другую версию нетронутой, но это довольно неприятно. - person John Zwinck; 02.10.2009
comment
@John Zwinck: я не могу представить, при каких обстоятельствах что-то сломается, если вы внесете это изменение. Это фактически то же самое, но на один процесс меньше. - person Teddy; 03.10.2009
comment
@Teddy: проблема в том, что некоторые люди делают это в интерактивной оболочке: . launcher.sh foo bar Если программа запуска сделает exec, оболочка пользователя завершится после завершения запущенной программы. Я знаю, что это странный вариант использования, но так оно и есть в существующей системе. - person John Zwinck; 05.10.2009
comment
@John Zwinck: сценарий можно переписать, чтобы определить, был ли он запущен или получен, и действовать соответствующим образом. - person Teddy; 20.10.2009

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

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

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

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

person Datageek    schedule 22.10.2011

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

* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh
person user377713    schedule 21.03.2012