Почему процессы, порожденные cron, в конечном итоге перестают функционировать?
-
19-09-2019 - |
Вопрос
У меня есть некоторые процессы, которые отображаются как <defunct>
в top
(и ps
).Я сварил все на основе реальных сценариев и программ.
В моем 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
) может быть изменен гораздо проще.
Решение
Потому что они не были предметом 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 разветвляется, и дочерний элемент cron запускает оболочку
- оболочка (1636) запускает sid и pgid 1636 и переходит в режим ожидания
- оболочка завершает работу, SIGCHLD отправляется в cron 3562
- сигнал игнорируется или неправильно обрабатывается
- шелл превращается в зомби.Обратите внимание, что режим sleep перенаправляется на init, поэтому при выходе из режима sleep init получит сигнал и выполнит очистку.Я все еще пытаюсь выяснить, когда пожнут зомби.Вероятно, при отсутствии активных дочерних элементов cron 1629 выясняет, что он может завершиться, в этот момент зомби будет переведен в init и получит reaped.Итак, теперь мы задаемся вопросом об отсутствующем SIGCHLD, который cron должен был обработать.
- Это не обязательно вина викси крон.Как вы можете видеть здесь, libdaemon устанавливает обработчик SIGCHLD во время
daemon_fork()
, и это может помешать доставке сигнала при быстром выходе промежуточным 1629Теперь я даже не знаю, собран ли vixie cron в моей системе Ubuntu с помощью libdaemon, но, по крайней мере, у меня есть новая теория.:-)
- Это не обязательно вина викси крон.Как вы можете видеть здесь, libdaemon устанавливает обработчик SIGCHLD во время
Другие советы
Я подозреваю, что Крон ждет всех подпроцессов в сессии, чтобы прекратить. См. Подождите (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.
Используйте командный набор (1). Вот тестор .sh:
#!/bin/bash
setsid sleep 27 # the real script launches a compiled C program in the background
Заметьте, что вам не нужно &
, SetSID помещает его на задний план.
На мой взгляд, это вызвано процессом Crond (порожденным Crond для каждой задачи) в ожидании ввода на stdin, который приводится к Stdout/Stderr команды в Crontab. Это сделано, потому что Cron может отправлять полученный результат по почте пользователю.
Таким образом, Кронд ждет EOF до команды пользователя, и все его порожденные дочерние процессы закрыли трубу. Если это сделано, Crond продолжается с помощью ожидания, а тогда несуществующая команда пользователя исчезает.
Поэтому я думаю, что вы должны явно отключить каждый порожденный подпроцесс в вашем скрипте с трубой (например, перенаправляя его в файл или /dev /null.
Таким образом, следующая строка должна работать в Crontab:
* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & )
Я бы порекомендовал вам решить проблему, просто не имея двух отдельных процессов: иметь launcher.sh
Сделайте это на последней строке:
exec "$@"
Это устранит лишний процесс.
Я нашел этот вопрос, пока искал решение с аналогичной проблемой. К сожалению, ответы в этом вопросе не решили мою проблему.
Убивание несуществующего процесса не является вариантом, так как вам нужно найти и убить его родительский процесс. Я закончил тем, что убил несуществующие процессы следующим образом:
ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh
В «grep» вы можете сузить поиск до определенного несуществующего процесса, который вы хотите.
Я так много раз тестировал ту же проблему. И, наконец, у меня есть решение. Просто укажите «/bin/bash» перед сценарием Bash, как показано ниже.
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh