php - 使用 exec() 时真的对命令进行守护进程吗?

标签 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) 创建一个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/

相关文章:

php - 使用两个类查询 PDO

php - Zend Framework如何将DbTable模型映射到数据库表并插入新行

java - 进程迁移

c - 如何杀死一个pid不断变化的进程?

带有获取文件内容并将数据插入数据库的php cron

php - 如何从 MySQL 列中提取数据并将其放入单个数组中

在 C/UNIX 中创建子进程/杀死进程

linux - 使用 bash,如何找到包含特定字符串的所有文件并将它们替换为现有文件?

java - 使用 ANT 脚本设置环境变量

PHP 执行外部命令 - 当需要用户名和密码时如何执行