Почему процессы, порожденные cron, перестают работать?

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

В моем crontab:

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

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

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

Содержимое 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 является общим script в системе, на которой выполняется эта операция, что не так легко изменить. Другие вещи (crontab, tester.sh, даже программа, которую я запускаю вместо sleep), могут быть легко модифицированы.

Ответы

Ответ 1

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

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

Ответ 2

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

exec "[email protected]"

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

Ответ 3

Я подозреваю, что 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 помещает его в фоновый режим.

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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