14

我有一些进程显示为<defunct>topps)。我已经从真实的脚本和程序中总结了一些东西。

在我的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)可以更容易地修改。

4

6 回答 6

14

因为它们不是wait(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

所以,发生的事情是,我认为:

  • cron forks 和 cron child 启动 shell
  • shell (1636) 启动 sid 和 pgid 1636 并开始睡眠
  • shell 退出,SIGCHLD 发送到 cron 3562
  • 信号被忽略或处理不当
  • 壳变成僵尸。请注意,sleep 被重新设置为 init,因此当 sleep 退出时,init 将获取信号并进行清理。我还在想知道僵尸什么时候收割。可能没有活动的孩子 cron 1629 计算出它可以退出,此时僵尸将被重新设置为 init 并获得收割。所以现在我们想知道 cron 应该处理的丢失的 SIGCHLD。
    • 这不一定是 vixie cron 的错。正如您在此处看到的,libdaemon在 期间安装了一个 SIGCHLD 处理程序daemon_fork(),这可能会干扰中间 1629 快速退出时的信号传递

      现在,我什至不知道我的 Ubuntu 系统上的 vixie cron 是否是用 libdaemon 构建的,但至少我有一个新的理论。:-)

于 2009-10-01T22:48:58.893 回答
8

在我看来,这是由进程 CROND(由 crond 为每个任务生成)等待 stdin 上的输入引起的,该输入通过管道传输到 crontab 中命令的 stdout/stderr。这样做是因为 cron 能够通过邮件将结果输出发送给用户。

所以 CROND 正在等待 EOF 直到用户命令并且它产生的所有子进程都关闭了管道。如果这样做了,CROND 继续等待语句,然后失效的用户命令消失。

所以我认为你必须明确断开脚本中每个生成的子进程与管道的连接(例如,通过将其重定向到文件或 /dev/null.

所以以下行应该在 crontab 中工作:

* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & ) 
于 2014-03-25T16:29:33.930 回答
4

我怀疑 cron 正在等待会话中的所有子进程终止。关于负 pid 参数,请参见 wait(2)。您可以通过以下方式查看 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 将其置于后台。

于 2009-10-01T23:29:25.093 回答
3

我建议您通过简单地没有两个单独的进程来解决问题:launcher.sh在最后一行执行此操作:

exec "$@"

这将消除多余的过程。

于 2009-10-01T23:00:16.863 回答
2

我在寻找具有类似问题的解决方案时发现了这个问题。不幸的是,这个问题的答案并没有解决我的问题。

杀死已失效的进程不是一种选择,因为您需要找到并杀死其父进程。我最终以以下方式杀死了已失效的进程:

ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh

在“grep ''”中,您可以将搜索范围缩小到您所追求的特定已失效进程。

于 2011-10-22T00:42:04.783 回答
-3

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

* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh
于 2012-03-21T02:52:47.333 回答