Bash 为什么cron产生的进程最终会失效?
我有一些进程在Bash 为什么cron产生的进程最终会失效?,bash,background,cron,zombie-process,defunct,Bash,Background,Cron,Zombie Process,Defunct,我有一些进程在top(和ps)中显示为。我从真实的脚本和程序中总结了一些东西 在我的crontab中: * * * * * /tmp/launcher.sh /tmp/tester.sh launcher.sh的内容(当然标记为可执行): tester.sh的内容(当然标记为可执行): ps显示以下内容: user 24257 24256 0 18:32 ? 00:00:00 [launcher.sh] <defunct> user 2425
top
(和ps
)中显示为
。我从真实的脚本和程序中总结了一些东西
在我的crontab
中:
* * * * * /tmp/launcher.sh /tmp/tester.sh
launcher.sh
的内容(当然标记为可执行):
tester.sh的内容(当然标记为可执行):
ps
显示以下内容:
user 24257 24256 0 18:32 ? 00:00:00 [launcher.sh] <defunct>
user 24259 1 0 18:32 ? 00:00:00 sleep 27
user 24257 24256 0 18:32?00:00:00[launcher.sh]
用户24259 1 0 18:32?00:00:00睡眠27
请注意,tester.sh
不会出现——它在启动后台作业后已退出
为什么launcher.sh
会粘在周围,标记为
?它似乎只有在由cron
启动时才会这样做,而不是在我自己运行它时
附加说明:launcher.sh
是运行该脚本的系统中的常见脚本,不易修改。其他东西(crontab
,tester.sh
,甚至是我运行的程序,而不是sleep
)可以更容易地修改。因为它们不是等待(2)
系统调用的主题
由于将来可能会有人等待这些进程,内核无法完全摆脱它们,或者无法执行wait
系统调用,因为它不再具有退出状态或存在的证据
当您从shell启动一个时,您的shell将捕获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
PPID-PID-PGID-SESS命令
1 3562 3562 3562 cron
3562 1629 3562 3562\\克朗
1629 1636 1636 1636\\ sh
1 1639 1636 1636睡眠
所以,我认为发生的是:
- cron分叉,cron子级启动shell
- shell(1636)启动sid和pgid 1636并启动睡眠
- 外壳退出,信号发送至cron 3562
- 信号被忽略或处理不当
- 贝壳变成了僵尸。请注意,sleep被重新分配给init,因此当sleep退出时,init将获得信号并进行清理。我还在想僵尸什么时候收割。可能在没有活动子级的情况下,cron 1629发现它可以退出,此时僵尸将被重新租给init并获得收获。所以现在我们想知道cron应该处理的丢失的SIGCHLD。这不一定是vixie cron的错。正如您在这里看到的,在
daemon_fork()
期间,这可能会干扰中间1629快速退出时的信号传递现在,我甚至不知道我的Ubuntu系统上的vixie cron是否是用libdaemon构建的,但至少我有了一个新的理论。:-)
我建议您通过不使用两个单独的进程来解决问题:Havelauncher.sh
在最后一行执行此操作:
exec "$@"
这将消除多余的进程。我怀疑cron正在等待会话中的所有子进程终止。有关负pid参数,请参见等待(2)。您可以通过以下方式查看SES:
ps faxo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
以下是我看到的(编辑):
请注意,您不需要&
,setsid将其放在后台。我在寻找类似问题的解决方案时发现了这个问题。不幸的是,这个问题的答案并没有解决我的问题
杀死不存在的进程不是一个选项,因为您需要找到并杀死它的父进程。我最终以以下方式终止了失效的进程:
ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh
ps-ef | grep'| grep-v grep | awk'{print“kill-9”,$3}sh
在“grep”中,您可以将搜索范围缩小到您所关注的特定已失效进程。我已经多次测试了相同的问题。
最后我得到了解决方案。
只需在bash脚本之前指定“/bin/bash”,如下所示
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh
****/bin/bash/tmp/launcher.sh/tmp/tester.sh
在我看来,这是由进程CROND(由CROND为每个任务生成)等待stdin上的输入引起的,stdin通过管道传输到crontab中命令的stdout/stderr。之所以这样做,是因为cron能够通过邮件将结果输出发送给用户
所以CROND一直在等待EOF,直到用户命令及其派生的所有子进程关闭管道。如果完成此操作,CROND将继续执行wait语句,然后失效的user命令将消失
因此,我认为您必须明确地从管道断开脚本中生成的每个子进程(例如,通过将其重定向到文件或/dev/null)
因此,以下行应该在crontab中工作:
* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & )
顺便说一句,标记为“
”的进程被称为“僵尸”。在这个线程中给出了一个可能的解决方案:它实际上会持续一整天,而不仅仅是直到cron醒来。你能对此发表意见吗?我运行的真正的程序(不是睡眠)运行数小时…是否有合适的解决方案?脚本是否可以做些什么来确保它完成后不会变成僵尸?嗨,你能告诉我如何重现此问题吗?可能生成输出的命令会导致“cron”作为一个僵尸?只是猜测。我认为你是对的,但我不能轻易做到这一点,因为<代码>发射器。SH <代码>被许多东西使用,如果我做了这个改变,其中一些会被破坏。我可以考虑制作一个新的启动脚本,它执行并留下完整的版本,但这是相当令人厌恶的。”John Zwinck:我无法想象。如果你做了这个更改,什么情况下事情会中断。实际上,只要少一个进程,这是同样的事情。@Teddy:会中断的事情是,有些人在交互式shell中这样做:.launcher.sh foo bar
,如果启动器这样做了
#!/bin/bash
setsid sleep 27 # the real script launches a compiled C program in the background
ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh
* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & )