从我的apache服务器的PHP页面,我使用如下行来运行一些命令:
exec("{$command} >> /tmp/test.log 2>&1 & echo -n \$!");
你可以看到论据here的解释。
但我不明白:如果我重新启动或停止我的apache服务器,我的命令也会死掉。
root@web2:/sx/temp# ps ax | grep 0ff | grep -v grep
15957 ? S 0:38 /usr/bin/php /sx/site_web_php/fr_FR/app/console task:exec /sx/temp/task_inventaire/ 0ff79bf690dcfdf788fff26c259882e2d07426df 10800
root@web2:/sx/temp# /etc/init.d/apache2 restart
Restarting web server: apache2 ... waiting ..
root@web2:/sx/temp# ps ax | grep 0ff | grep -v grep
root@web2:/sx/temp#
经过一些研究,我读到了一些有关父pid的内容,但在我的命令行中使用了&
,我认为我的父进程正在从他的父进程中删除。
我正在使用apache2和libapache2-mod-php5以及apache2-mpm-prefork。
我怎样才能真正将我的孩子程序与apache分离?
您可以通过以下方式在Linux / Mac上重现它:
a)创建一个包含以下内容的execution_script.php文件:
<?php
sleep(10);
b)创建一个包含以下内容的execute_from_http.php文件:
<?php
exec("php executed_script.php > /tmp/test.log 2>&1 & echo -n \$!");
c)运行http://localhost/path/execute_from_http.php
d)在终端上运行命令:
ps axjf | grep execute | grep -v grep ; sudo /etc/init.d/apache2 restart ; ps axjf | grep execute | grep -v grep
如果在execute_from_http.php脚本的10秒内运行该命令,您将获得输出:
php@beast:/var/www/xxx/$ ps axjf | grep execute | grep -v grep ; sudo /etc/init.d/apache2 restart ; ps axjf | grep execute | grep -v grep
1 5257 5245 5245 ? -1 S 33 0:00 php executed_script.php
* Restarting web server apache2
... waiting ...done.
php@beast:/var/www/xxx/$
正如您所看到的,ps
命令只输出一次,这告诉您在重新启动apache时执行的脚本已经死亡。
答案 0 :(得分:2)
我找到了一个有效的解决方案,但如果我们说出性能和安全性,我就不知道这是否合适。它使用at
命令,这种命令只运行一次。
而不是:
exec("php executed_script.php > /dev/null 2>&1 & echo -n \$!");
使用:
exec("echo 'php executed_script.php > /dev/null 2>&1' | at now -M");
关键是executed_script.php
将由外部守护程序(atd)运行,因此executed_script.php
将是atd
的子项,而不是apache的子项。< / p>
php@beast:/var/www/xxx$ ps axjf | grep execute | grep -v grep ; sudo /etc/init.d/apache2 restart ; ps axjf | grep execute | grep -v grep
7032 7033 973 973 ? -1 SN 33 0:00 \_ php executed_script.php
* Restarting web server apache2
... waiting ...done.
7032 7033 973 973 ? -1 SN 33 0:00 \_ php executed_script.php
php@beast:/var/www/xxx$ ps ax | grep 973
973 ? Ss 0:00 atd
注意几件事:
$!
,那么您将获得at
的pid。www-data
中默认情况下的/etc/at.deny
(可能存在原因,请注意)at
在atd
读取的文件上写下来进行沟通正如@ hek2mgl在自己的回答中写道,我们可以使用pcntl_fork()
,但这不是那么简单。首先,您无法在apache后面运行pcntl_fork()
,因为如果我们查看PHP手册Introduction of the Process Control,我们可以看到:
不应在Web服务器环境中启用Process Control 如果有任何过程控制功能,可能会发生意外结果 在Web服务器环境中使用。
制作fork时,您将在内存中获得父进程的两个精确副本。并且因为apache后面的PHP作为模块运行,所以在PHP执行结束时(即使在die()
之后),你回到apache的模块包装器,你无法控制发生了什么事。
所以这里是一个带有中间命令的场景,它将守护你的执行:
1)在Apache中,运行将创建守护命令的中间命令:
$command = escapeshellarg("php executed_script.php");
exec("php run_as_daemon.php {$command} >> /dev/null 2>&1 &");
2)中间命令fork并使用posix_setsid
真正分离命令。
<?php
if (!isset($argv[1]))
{
exit;
}
$command = $argv[1];
$pid = pcntl_fork();
if ($pid < 0) // error
exit;
else if ($pid) // parent
exit;
else // child
{
$sid = posix_setsid(); // creates a daemon
if ($sid < 0)
exit;
exec("{$command} >> /dev/null 2>&1 &");
}
3)你执行的命令当然不会改变:
<?php
sleep(10);
结果:
php@beast:/var/www/xxx/$ wget -qO- http://localhost/xxx/execute_from_http.php && sleep 1 && ps axjf | grep execute | grep -v grep ; sudo /etc/init.d/apache2 restart ; ps axjf | grep execute | grep -v grep
1 19958 19956 19956 ? -1 S 33 0:00 php executed_script.php
* Restarting web server apache2 ......done.
1 19958 19956 19956 ? -1 S 33 0:00 php executed_script.php
答案 1 :(得分:1)
首先请注意,'&amp;'在你的例子中只是一个布尔AND,它连接命令和 echo 。如果要在后台中启动命令,意味着exec将立即返回,请使用命令行末尾的&
:
exec("{$command} >> /tmp/test.log 2>&1 & echo -n \$! &");
如果您希望在apache完成后运行该进程,则必须使用pcntl_fork()
这是一个例子:
$pid = pcntl_fork();
switch($pid) {
case -1 : die ('Error while forking');
case 0: // daemon code
posix_setsid(); // create new process group
exec("{$command} >> /tmp/test.log 2>&1 & echo -n \$!");
break;
default:
echo 'daemon started';
break;
}
现在,启动PHP脚本中没有代码可以处理exec
的返回值及其输出。因此,当前进程可以在exec
完成之前完成。在此之后,工作进程将由init
拥有。
您还可以查看PEAR包System_Daemon
。这有助于守护脚本。