使用exec()时真的守护命令?

时间:2013-04-12 15:53:33

标签: php process exec parent-child daemon

从我的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时执行的脚本已经死亡。

2 个答案:

答案 0 :(得分:2)

&#34; at&#34;方法

我找到了一个有效的解决方案,但如果我们说出性能和安全性,我就不知道这是否合适。它使用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

注意几件事:

  • 您无法访问已运行应用的pid,如果您在我之前的代码中得到$!,那么您将获得at的pid。
  • 您需要删除www-data中默认情况下的/etc/at.deny(可能存在原因,请注意)
  • 我对性能有严重怀疑:我认为atatd读取的文件上写下来进行沟通

fork / setsid方法

正如@ 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。这有助于守护脚本。