Почему процессы, порожденные cron, перестают работать?
У меня есть некоторые процессы, которые отображаются как <defunct>
в top
(и ps
). Я откинулся от реальных сценариев и программ.
В моем 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