疯狂的 crond 行为。不断使 bash 进程失效
我有一个看起来像这样的 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 脚本挂起
I have a crontab that looks like:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
0-59 * * * * /var/www/html/private/fivemin/zdaemon.php >> /dev/null &
Simple as possible, right?
zdaemon.php which I am just testing with is:
#!/usr/bin/php
<?
while(true){
sleep(1);
}
?>
Whenever it runs it hangs like:
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
I have been banging my brain against a wall on this all day. Has anyone seen this before? Any ideas at all?
This is a reference to: Init.d script hanging
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
僵尸进程本身并不一定是坏事。它表明子进程已经死亡,父进程尚未获得其状态(使用
wait()
或相关系统调用)。发生的情况如下 -
cron
对它启动的脚本中的 stderr 感兴趣(以便在脚本失败时它可以通过电子邮件发送给您),因此它创建了一个pipe 附加脚本的 stderr 来写入结尾(文件描述符 2)。然后,cron
坐在管道的读取端进行读取,等待脚本退出并读取 eof(read()
为零字节) - 然后它获取脚本的返回状态。在您的示例中,生成的守护进程继承了 stderr 文件描述符,因此当中间 shell 退出(并失效)时,管道由守护进程保持打开状态。因此,
cron
永远不会读取 eof,因此永远不会获得返回状态。解决方案是确保守护进程的 stderr 已关闭。这可以通过以下方式实现:
它将两者 stdout 和 stderr 写入
/dev/null
A zombie process is not necessarily a bad thing in itself. It indicates that the child process has died, and the parent process has not yet reaped its status (using
wait()
or a related system call).What is happening is as follows -
cron
is interested in stderr from the script it starts (so that it can email it to you should the script fail), therefore it creates a pipe which is attaches stderr of the script to write end (file descriptor 2). Thencron
sits reading on the read end of the pipe, waiting for the script to exit and read eof (read()
of zero bytes) - it then reaps the return status of the script.In your example, the daemon spawned, inherits the stderr file descriptor, and therefore when the intermediate shell exits (and becomes defunct), the pipe is held open by the daemon. Therefore
cron
never reads eof and hence never reaps the return status.The solution is to ensure that stderr of your daemon is closed. This can be achieved as follows:
which will write both stdout and stderr to
/dev/null
我认为你的主要问题是 stderr 仍然进入 shell,但子进程(你的 php 进程)正在睡眠,导致僵尸进程。尝试一下:
如果您仍然遇到僵尸进程问题,请查看 nohup 。
I think your main problem is that the stderr is still going to the shell but the child process (your php process) is sleeping, resulting in the zombie process. Try this:
If you're still having problems with a zombie process, take a look at nohup.
在 crontab 中设置一个进程的后台对我来说似乎很奇怪。尝试删除行尾的
&
。It seems odd to me to background a process in a crontab. Try removing the
&
at the end of the line.创建守护进程的通常方法是分叉一个子进程来完成工作,然后以错误代码 0 退出父进程。不过,我不确定这是否是您的问题。
我还没有在 php 中这样做,但你可以使用 pcntl_fork() 模仿通常的c方式。
The usual way to create a daemon is to fork a child process to do the work, then exit the parent process with an error code of 0. I don't know for sure that this is your problem, though.
I haven't done this in php, but you could use pcntl_fork() to mimic the usual c way.