我有一些流程显示为 <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醒来。


更新: 回应评论...嗯。我确实设法复制了这个问题:

 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叉和克朗孩子开始贝壳
  • Shell(1636)开始SID和PGID 1636并开始睡眠
  • 外壳出口,sigchld发送到Cron 3562
  • 信号被忽略或不当
  • 壳变成僵尸。请注意,睡眠是对初始化的修复,因此,当睡眠退出时,初始化将获得信号并清理。我仍在努力弄清楚僵尸何时获得收获。大概没有活跃的孩子克朗1629的数字可以退出,那时,僵尸将被调整以启动并获得收获。因此,现在我们想知道Cron应该处理的缺失的Sigchld。
    • 它不一定是Vixie Cron的错。正如您在这里看到的那样 libdaemon安装一个sigchld处理程序 期间 daemon_fork(), ,这可能会干扰中级1629的快速出口上的信号传递

      现在,我什至不知道我的Ubuntu系统上的Vixie Cron是否甚至是由Libdaemon构建的,但至少我有一个新的理论。 :-)

其他提示

我怀疑克朗正在等待会话中的所有子过程终止。有关负PID论点,请参见(2)。您可以看到:

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和睡眠在同一情节中。

使用命令setSID(1)。这是tester.sh:

#!/bin/bash
setsid sleep 27 # the real script launches a compiled C program in the background

注意您不需要 &, ,SetSID将其放在后台。

我认为,这是由过程Crond引起的(Crond为每任任务催生)等待STDIN上的输入,该输入被管道输入到Crontab中命令的Stdout/stderr。之所以这样做,是因为Cron能够通过邮件将结果输出发送给用户。

因此,Crond一直在等待EOF,直到用户命令及其所产卵的所有过程都关闭了管道。如果完成此操作,则crond继续使用等待statement,然后消失的用户命令消失。

因此,我认为您必须明确断开脚本中的每个产卵子过程的连接,例如,将其重定向到文件或 /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'”中,您可以将搜索范围缩小到所追求的特定已停产过程。

我已经测试了很多次相同的问题。最后我有了解决方案。如下所示,只需在BASH脚本之前指定“/bin/bash”。

* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top