1

我有一个看起来像的 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 脚本挂起

4

4 回答 4

10

僵尸进程本身不一定是坏事。它表示进程已经死亡,而进程还没有获得它的状态(使用wait()或相关的系统调用)。

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

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

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

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

会将stdoutstderr写入/dev/null

于 2010-09-20T09:16:39.950 回答
2

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

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

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

于 2010-09-20T03:00:56.997 回答
2

在 crontab 中设置进程的背景对我来说似乎很奇怪。尝试删除&行尾的 。

于 2010-09-20T06:31:10.443 回答
0

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

我没有在 php 中做到这一点,但你可以使用pcntl_fork()来模仿通常的 c 方式

于 2010-09-20T03:12:11.363 回答