为什么cron产生的进程最终不存在?

Joh*_*nck 14 bash cron background zombie-process defunct

我有一些进程显示<defunct>top(和ps)中.我从真实的脚本和程序中榨取了一些东西.

在我的crontab:

* * * * * /tmp/launcher.sh /tmp/tester.sh
Run Code Online (Sandbox Code Playgroud)

的内容launcher.sh(这是当然的标记为可执行):

#!/bin/bash
# the real script does a little argument processing here
"$@"
Run Code Online (Sandbox Code Playgroud)

的内容tester.sh(这是当然的标记为可执行):

#!/bin/bash
sleep 27 & # the real script launches a compiled C program in the background
Run Code Online (Sandbox Code Playgroud)

ps 显示以下内容:

user       24257 24256  0 18:32 ?        00:00:00 [launcher.sh] <defunct>
user       24259     1  0 18:32 ?        00:00:00 sleep 27
Run Code Online (Sandbox Code Playgroud)

请注意,tester.sh它不会出现 - 它在启动后台作业后退出.

为什么要launcher.sh坚持,标记<defunct>?它似乎只是在启动cron时执行此操作 - 而不是在我自己运行它时.

附加说明:launcher.sh是运行的系统中的常见脚本,不容易修改.其他的东西(crontab,tester.sh甚至是我运行的程序,而不是sleep)可以更容易地修改.

Dig*_*oss 13

因为它们还没有成为wait(2)系统调用的主题.

由于某人可能会在将来等待这些进程,因此内核无法完全摆脱它们,或者它无法执行wait系统调用,因为它不再具有退出状态或其存在的证据.

当你从shell启动一个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
Run Code Online (Sandbox Code Playgroud)

那么,发生了什么事,我想:

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

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

  • ..这是否有适当的解决方案?脚本可以做些什么来确保它完成时不会变成僵尸吗? (3认同)

小智 5

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

因此CROND正在等待EOF,直到用户命令及其所有产生的子进程关闭了管道。如果完成此操作,CROND会继续等待语句,然后消失的用户命令将消失。

因此,我认为您必须从管道中显式断开脚本中所有产生的子进程的连接(例如,将其重定向到文件或/ dev / null。

因此以下行应在crontab中工作:

* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & ) 
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,这篇文章给了我半夜的快乐。 (2认同)