代码之家  ›  专栏  ›  技术社区  ›  John Zwinck

为什么cron产生的进程最终会失效?

  •  14
  • John Zwinck  · 技术社区  · 15 年前

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

    聚苯乙烯 显示以下内容:

    user       24257 24256  0 18:32 ?        00:00:00 [launcher.sh] <defunct>
    user       24259     1  0 18:32 ?        00:00:00 sleep 27
    

    注意 试验室 不显示--它在启动后台作业后退出。

    为什么? 洗衣房 贴在周围,有标记 <失效; ?似乎只有在 cron --不是我自己经营的时候。

    附加说明: 洗衣房 是运行此脚本的系统中的常见脚本,不容易修改。其他的事情( 克伦塔布 , 试验室 ,甚至是我运行的程序,而不是 sleep )更容易修改。

    6 回复  |  直到 10 年前
        1
  •  11
  •   DigitalRoss    15 年前

    因为他们不是 wait(2) 系统调用。

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

    当您从shell启动一个时,shell会捕获sigchld并无论如何执行各种等待操作,因此没有什么东西会长期失效。

    但是克罗恩并没有处于等待状态,他正在睡觉,所以这个死去的孩子可能会在一旁呆一段时间,直到克罗恩醒来。


    更新: 正在答复评论… 嗯,我确实设法复制了这个问题:

     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并启动sleep
    • shell退出,sigchld发送到cron 3562
    • 信号被忽略或处理不当
    • 贝壳变成僵尸。请注意,sleep被重新设置为init,因此当sleep退出时,init将收到信号并清除。我还想知道僵尸什么时候会被收割。可能没有活动的孩子,cron 1629发现它可以退出,此时僵尸将被重新安置到init并获得收获。所以现在我们想知道Cron应该处理的丢失的sigchld。
      • 这不一定是韦克谢克罗恩的错。如你所见, libdaemon installs a SIGCHLD handler 在期间 daemon_fork() ,这可能会干扰1629年中期快速出口的信号传输。

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

        2
  •  4
  •   bstpierre Edgar Aviles    15 年前

    我怀疑cron正在等待会话中的所有子进程终止。关于负PID参数,请参见等待(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将其置于后台。

        3
  •  4
  •   hp4    10 年前

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

    所以crond正在等待eof,直到用户命令和它生成的所有子进程关闭管道。如果这样做了,crond将继续使用wait语句,然后失效的用户命令将消失。

    因此,我认为您必须从管道中显式地断开脚本中生成的每个子进程(例如,通过将其重定向到一个文件或/dev/null)。

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

    * * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & ) 
    
        4
  •  3
  •   Teddy    15 年前

    i_d建议您通过不使用两个单独的过程来解决问题:have launcher.sh 在最后一行执行此操作:

    exec "$@"
    

    这将消除多余的过程。

        5
  •  1
  •   Datageek    13 年前

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

    杀死失效的进程不是一个选项,因为您需要找到并杀死它的父进程。我以以下方式结束了这些已失效的进程:

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

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

        6
  •  -2
  •   user377713    12 年前

    我对同一个问题做了很多次测试。 最后我找到了解决办法。 只需在bash脚本之前指定“/bin/bash”,如下所示。

    * * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh