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构建的,但至少我有了一个新的理论。:-)


我建议您通过不使用两个单独的进程来解决问题:Have
launcher.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 & )