【问题标题】:Insane crond behavior. keeps making defunct bash processes疯狂的 crond 行为。不断制作失效的bash进程
【发布时间】:2011-04-14 11:43:31
【问题描述】:

我有一个看起来像这样的 crontab:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

0-59 * * * * /var/www/html/private/fivemin/zdaemon.php >> /dev/null &

尽可能简单,对吧?

我正在测试的

zdaemon.php 是:

#!/usr/bin/php
<?


while(true){
        sleep(1);
}

?>

无论何时运行,它都会像这样挂起:

root     15532  0.0  0.1 57228 1076 ?        Ss   19:09   0:00 crond
root     16681  0.0  0.1 72196 1428 ?        S    21:46   0:00 crond
root     16682  0.0  0.0     0    0 ?        Zs   21:46   0:00 [bash] <defunct>
root     16683  0.0  0.5 54800 5740 ?        S    21:46   0:00 /usr/bin/php /var/www/html/private/fivemin/zdaemon.php
root     16687  0.0  0.1 72196 1428 ?        S    21:47   0:00 crond
root     16688  0.0  0.0     0    0 ?        Zs   21:47   0:00 [bash] <defunct>
root     16689  0.0  0.5 54800 5740 ?        S    21:47   0:00 /usr/bin/php /var/www/html/private/fivemin/zdaemon.php

我整天都在用脑子敲墙。有没有人见过这个?有什么想法吗?

这是参考:Init.d script hanging

【问题讨论】:

    标签: php bash centos cron crontab


    【解决方案1】:

    僵尸进程本身并不一定是坏事。它表明 child 进程已经死亡,而 parent 进程尚未恢复其状态(使用wait() 或相关的系统调用)。

    发生的情况如下 - cron 对它启动的脚本中的 stderr 感兴趣(以便它可以在脚本失败时通过电子邮件将其发送给您),因此它创建了一个 pipe 将脚本的 stderr 附加到写入端(文件描述符 2)。然后cron 坐在管道的读取端读取,等待脚本退出并读取 eofread() 零字节) - 然后它获取脚本的返回状态。

    在您的示例中,生成的守护程序继承了 stderr 文件描述符,因此当中间 shell 退出(并失效)时,管道由守护程序保持打开状态。因此cron 永远不会读取 eof,因此永远不会获得返回状态。

    解决方案是确保您的守护程序的 stderr 已关闭。这可以通过以下方式实现:

    0-59 * * * * /var/www/html/private/fivemin/zdaemon.php >> /dev/null 2>&1 &
    

    这会将 both stdoutstderr 写入/dev/null

    【讨论】:

    • 这太完美了!!是 100% 的 stderr 把我搞砸了。我将行更改为:$LDIR/$EXEC &> $LDIR/swapi.log & MYPID=$!在我的 init.d 脚本中(请参阅其他帖子),现在一切正常。谢谢谢谢谢谢!!!
    【解决方案2】:

    在 crontab 中将进程设置为后台对我来说似乎很奇怪。尝试删除行尾的&amp;

    【讨论】:

      【解决方案3】:

      我认为您的主要问题是 stderr 仍在进入 shell 但子进程(您的 php 进程)正在休眠,从而导致僵尸进程。试试这个:

      0-59 * * * * /var/www/html/private/fivemin/zdaemon.php &> /dev/null &
      

      如果您仍然遇到僵尸进程问题,请查看nohup

      【讨论】:

        【解决方案4】:

        创建守护进程的常用方法是派生一个子进程来完成工作,然后以错误代码 0 退出父进程。不过,我不确定这是您的问题。

        我没有在 php 中这样做,但你可以使用 pcntl_fork() 来模仿 the usual c way

        【讨论】:

        • 需要注意的是,使用 fork() 启动一个守护进程并非易事,通常需要 fork 两次才能正确完成。
        猜你喜欢
        • 2010-12-01
        • 2010-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多