我有一个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
这一整天,我一直在撞墙。谁看过这个吗?有什么想法吗?
的引用答案 0 :(得分:8)
zombie 进程本身并不一定是坏事。它表示子进程已经死亡,父进程尚未获得其状态(使用wait()
或相关的系统调用)。
正在发生的事情如下 - cron
对它启动的脚本中的 stderr 感兴趣(如果脚本失败,它可以通过电子邮件发送给您),因此它会创建一个 pipe ,它附加脚本的 stderr 以写入结尾(文件描述符2)。然后cron
坐在读取管道的读取端,等待脚本退出并读取 eof (零{/ 1}}零字节) - 然后它收获返回状态脚本。
在您的示例中,生成的守护程序继承了 stderr 文件描述符,因此当中间shell退出(并且变得不存在)时,管道将由守护程序保持打开状态。因此,read()
永远不会读取 eof ,因此永远不会获得返回状态。
解决方案是确保您的守护程序的 stderr 已关闭。这可以通过以下方式实现:
cron
将 stdout 和 stderr 写入0-59 * * * * /var/www/html/private/fivemin/zdaemon.php >> /dev/null 2>&1 &
答案 1 :(得分:2)
我认为你的主要问题是stderr仍然是shell,但子进程(你的php进程)正在休眠,导致僵尸进程。试试这个:
0-59 * * * * /var/www/html/private/fivemin/zdaemon.php &> /dev/null &
如果您仍然遇到僵尸进程问题,请查看nohup。
答案 2 :(得分:2)
在crontab中对一个进程进行后台处理似乎很奇怪。尝试删除该行末尾的&
。
答案 3 :(得分:0)
创建守护进程的常用方法是派生子进程执行工作,然后退出父进程,错误代码为0.我不确定这是否是您的问题。
我没有在php中完成此操作,但您可以使用pcntl_fork()来模仿the usual c way。