从我的 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) 创建一个execute_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 重新启动时死亡。
最佳答案
“at”方法
我找到了一个可行的解决方案,但我不知道如果我们谈论性能和安全性,这是否可以。它使用 at
命令,一种只运行一次的 cron。
而不是:
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
的 child 而不是 apache 的。
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
注意几件事:
- 如果您收到
$!
,则无法访问已运行应用程序的 pid就像我之前的代码片段一样,您将获得at
的 pid . - 您需要删除
www-data
默认情况下为/etc/at.deny
(可能是有原因的,所以要小心) - 我对性能有严重怀疑:我认为
at
写入atd
读取的文件沟通
fork/setsid 方法
正如 @hek2mgl 在自己的答案中所写,我们可以使用 pcntl_fork()
,但这并不那么简单。首先,你不能运行pcntl_fork()
落后于 apache,因为如果我们查看 PHP 手册,Introduction of the Process Control ,我们可以看到:
Process Control should not be enabled within a web server environment and unexpected results may happen if any Process Control functions are used within a web server environment.
当进行 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
关于php - 使用 exec() 时真的对命令进行守护进程吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15975777/