php执行后台进程

时间:2008-09-05 14:43:48

标签: php

我需要在用户操作时执行目录副本,但目录非常大,所以我希望能够执行此类操作,而无需用户知道副本完成所需的时间。

我们非常感谢任何建议。

19 个答案:

答案 0 :(得分:342)

假设这是在Linux机器上运行的,我总是像这样处理它:

exec(sprintf("%s > %s 2>&1 & echo $! >> %s", $cmd, $outputfile, $pidfile));

这将启动命令$cmd,将命令输出重定向到$outputfile,并将进程ID写入$pidfile

这使您可以轻松监控进程正在执行的操作以及它是否仍在运行。

function isRunning($pid){
    try{
        $result = shell_exec(sprintf("ps %d", $pid));
        if( count(preg_split("/\n/", $result)) > 2){
            return true;
        }
    }catch(Exception $e){}

    return false;
}

答案 1 :(得分:22)

将进程编写为服务器端脚本,无论使用何种语言(php / bash / perl / etc)都很方便,然后从php脚本中的进程控制函数中调用它。

该函数可能检测到标准io是否用作输出流,如果是,则设置返回值..如果不是则结束

Proc_Close (Proc_Open ("./command --foo=1 &", Array (), $foo));

我使用“sleep 25s”作为命令从命令行快速测试了它,它就像一个魅力。

Answer found here

答案 2 :(得分:19)

您可能想尝试将此附加到您的命令

>/dev/null 2>/dev/null &

例如

shell_exec('service named reload >/dev/null 2>/dev/null &');

答案 3 :(得分:18)

我想在Windows上添加一个非常简单的示例来测试此功能:

创建以下两个文件并将其保存到Web目录:

foreground.php:

<?php

ini_set("display_errors",1);
error_reporting(E_ALL);

echo "<pre>loading page</pre>";

function run_background_process()
{
    file_put_contents("testprocesses.php","foreground start time = " . time() . "\n");
    echo "<pre>  foreground start time = " . time() . "</pre>";

    // output from the command must be redirected to a file or another output stream 
    // http://ca.php.net/manual/en/function.exec.php

    exec("php background.php > testoutput.php 2>&1 & echo $!", $output);

    echo "<pre>  foreground end time = " . time() . "</pre>";
    file_put_contents("testprocesses.php","foreground end time = " . time() . "\n", FILE_APPEND);
    return $output;
}

echo "<pre>calling run_background_process</pre>";

$output = run_background_process();

echo "<pre>output = "; print_r($output); echo "</pre>";
echo "<pre>end of page</pre>";
?>

background.php:

<?
file_put_contents("testprocesses.php","background start time = " . time() . "\n", FILE_APPEND);
sleep(10);
file_put_contents("testprocesses.php","background end time = " . time() . "\n", FILE_APPEND);
?>

授予IUSR写入您创建上述文件的目录的权限

授予IUSR读取和执行C:\ Windows \ System32 \ cmd.exe

的权限

从网络浏览器点击foreground.php

以下内容应该呈现给浏览器w /输出数组中的当前时间戳和本地资源#:

loading page
calling run_background_process
  foreground start time = 1266003600
  foreground end time = 1266003600
output = Array
(
    [0] => 15010
)
end of page

你应该在保存上述文件的同一目录中看到testoutput.php,它应该是空的

您应该在保存上述文件的同一目录中看到testprocesses.php,它应该包含以下文本和当前时间戳:

foreground start time = 1266003600
foreground end time = 1266003600
background start time = 1266003600
background end time = 1266003610

答案 4 :(得分:10)

如果您需要在没有PHP页面等待它完成的情况下在后台执行某些操作,您可以使用wget命令“调用”的另一个(后台)PHP脚本。当然,这个后台PHP脚本将以您的系统上的任何其他PHP脚本的权限执行。

以下是使用gnuwin32软件包中的wget的Windows示例。

后台代码(文件test-proc-bg.php)作为例子...

sleep(5);   // some delay
file_put_contents('test.txt', date('Y-m-d/H:i:s.u')); // writes time in a file

前台脚本,一个调用...

$proc_command = "wget.exe http://localhost/test-proc-bg.php -q -O - -b";
$proc = popen($proc_command, "r");
pclose($proc);

您必须使用popen / pclose才能正常工作。

wget选项:

-q    keeps wget quiet.
-O -  outputs to stdout.
-b    works on background

答案 5 :(得分:6)

我发现使用的版本更快更容易

shell_exec('screen -dmS $name_of_screen $command'); 

它有效。

答案 6 :(得分:6)

这是一个在PHP中启动后台进程的函数。在经过大量阅读和测试不同的方法和参数之后,最终创建了一个实际上也适用于Windows的工具。

function LaunchBackgroundProcess($command){
  // Run command Asynchroniously (in a separate thread)
  if(PHP_OS=='WINNT' || PHP_OS=='WIN32' || PHP_OS=='Windows'){
    // Windows
    $command = 'start "" '. $command;
  } else {
    // Linux/UNIX
    $command = $command .' /dev/null &';
  }
  $handle = popen($command, 'r');
  if($handle!==false){
    pclose($handle);
    return true;
  } else {
    return false;
  }
}

注1:在Windows上,请勿使用其他地方建议的/B参数。它强制进程与start命令本身一起运行相同的控制台窗口,从而导致进程同步处理。要在单独的线程中运行该进程(异步),请不要使用/B

注意2:如果命令是引用路径,则需要start ""之后的空双引号。 start命令将第一个引用的参数解释为窗口标题。

答案 7 :(得分:4)

你能安排分开一个单独的进程,然后在后台运行你的副本吗?我做了任何PHP已经有一段时间了,但函数pcntl-fork看起来很有希望。

答案 8 :(得分:3)

您可以尝试像Resque这样的排队系统。然后,您可以生成一个处理信息的作业,并使用“处理”图像快速返回。使用这种方法,你不会知道它何时完成。

此解决方案适用于大型应用程序,您不希望前端机器执行繁重的工作,因此可以处理用户请求。 因此,它可能会或可能不会与文件和文件夹等物理数据一起使用,但是为了处理更复杂的逻辑或其他异步任务(即新的注册邮件),拥有并且可扩展性很好。

答案 9 :(得分:2)

使用此功能在后台运行程序。它跨平台,完全可定制。

<?php
function startBackgroundProcess(
    $command,
    $stdin = null,
    $redirectStdout = null,
    $redirectStderr = null,
    $cwd = null,
    $env = null,
    $other_options = null
) {
    $descriptorspec = array(
        1 => is_string($redirectStdout) ? array('file', $redirectStdout, 'w') : array('pipe', 'w'),
        2 => is_string($redirectStderr) ? array('file', $redirectStderr, 'w') : array('pipe', 'w'),
    );
    if (is_string($stdin)) {
        $descriptorspec[0] = array('pipe', 'r');
    }
    $proc = proc_open($command, $descriptorspec, $pipes, $cwd, $env, $other_options);
    if (!is_resource($proc)) {
        throw new \Exception("Failed to start background process by command: $command");
    }
    if (is_string($stdin)) {
        fwrite($pipes[0], $stdin);
        fclose($pipes[0]);
    }
    if (!is_string($redirectStdout)) {
        fclose($pipes[1]);
    }
    if (!is_string($redirectStderr)) {
        fclose($pipes[2]);
    }
    return $proc;
}

请注意,在命令启动后,默认情况下此函数会关闭正在运行的进程的stdin和stdout。您可以通过$ redirectStdout和$ redirectStderr参数将进程输出重定向到某个文件。

Windows用户注意事项:
您无法通过以下方式将stdout / stderr重定向到nul

startBackgroundProcess('ping yandex.com', null, 'nul', 'nul');

但是,你可以这样做:

startBackgroundProcess('ping yandex.com >nul 2>&1');

* nix用户注释:

1)如果你想得到实际的PID,请使用exec shell命令:

$proc = startBackgroundProcess('exec ping yandex.com -c 15', null, '/dev/null', '/dev/null');
print_r(proc_get_status($proc));

2)如果要将一些数据传递给程序的输入,请使用$ stdin参数:

startBackgroundProcess('cat > input.txt', "Hello world!\n");

答案 10 :(得分:2)

Windows和Linux的可行解决方案。在My github page上了解详情。

function run_process($cmd,$outputFile = '/dev/null', $append = false){
                    $pid=0;
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        $cmd = 'wmic process call create "'.$cmd.'" | find "ProcessId"';
                        $handle = popen("start /B ". $cmd, "r");
                        $read = fread($handle, 200); //Read the output 
                        $pid=substr($read,strpos($read,'=')+1);
                        $pid=substr($pid,0,strpos($pid,';') );
                        $pid = (int)$pid;
                        pclose($handle); //Close
                }else{
                    $pid = (int)shell_exec(sprintf('%s %s %s 2>&1 & echo $!', $cmd, ($append) ? '>>' : '>', $outputFile));
                }
                    return $pid;
            }
            function is_process_running($pid){
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                        //tasklist /FI "PID eq 6480"
                    $result = shell_exec('tasklist /FI "PID eq '.$pid.'"' );
                    if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                        return true;
                    }
                }else{
                    $result = shell_exec(sprintf('ps %d 2>&1', $pid));
                    if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) {
                        return true;
                    }
                }
                return false;
            }
            function stop_process($pid){
                    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {//'This is a server using Windows!';
                            $result = shell_exec('taskkill /PID '.$pid );
                        if (count(preg_split("/\n/", $result)) > 0 && !preg_match('/No tasks/', $result)) {
                            return true;
                        }
                    }else{
                            $result = shell_exec(sprintf('kill %d 2>&1', $pid));
                        if (!preg_match('/No such process/', $result)) {
                            return true;
                        }
                    }
            }

答案 11 :(得分:1)

创建触发器文件并使用cron或autosys之类的调度程序定期执行查找并作用于触发器文件的脚本,而不是启动后台进程?触发器可能包含指令甚至原始命令(更好的是,只需将其作为shell脚本)。

答案 12 :(得分:1)

如果使用PHP,使用pcntl_fork有一种更简单的方法:

http://www.php.net/manual/en/function.pcntl-fork.php

答案 13 :(得分:1)

我正在大量使用fast_cgi_finish_request()

与闭包和register_shutdown_function()

结合使用
$message ='job executed';
$backgroundJob = function() use ($message) {
     //do some work here
    echo $message;
}

然后在关闭之前注册此闭包。

register_shutdown_function($backgroundJob);

最后,当响应发送到客户端时,您可以关闭与客户端的连接并继续使用PHP进程:

fast_cgi_finish_request();

闭包将在fast_cgi_finish_request之后执行。

$ message将随时无法显示。您可以根据需要注册尽可能多的闭包,但要注意脚本执行时间。 这只有在PHP作为快速CGI模块运行时才有效(是吗?!)

答案 14 :(得分:0)

PHP脚本与其他桌面应用程序开发语言不同。在桌面应用程序语言中,我们可以设置守护程序线程来运行后台进程,但在PHP中,当用户请求页面时会发生进程。但是,可以使用php脚本运行的服务器的cron作业功能设置后台作业。

答案 15 :(得分:0)

对于我们这些使用 Windows 的人,请看一下:

参考:http://php.net/manual/en/function.exec.php#43917

  

我也在努力让一个程序在后台运行   脚本继续执行时的Windows。这个方法不像   其他解决方案允许您启动最小化,最大化的程序,   或者根本没有窗户。 llbra @ phpbrasil的解决方案确实有效   有时你真的会在桌面上产生一个不需要的窗口   希望任务运行隐藏。

启动Notepad.exe在后台最小化:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("notepad.exe", 7, false); 
?> 

在后台启动一个不可见的shell命令:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("cmd /C dir /S %windir%", 0, false); 
?> 

启动MSPaint最大化并等待你关闭它然后继续脚本:

<?php 
$WshShell = new COM("WScript.Shell"); 
$oExec = $WshShell->Run("mspaint.exe", 3, true); 
?> 

有关Run()方法的更多信息,请访问: http://msdn.microsoft.com/library/en-us/script56/html/wsMthRun.asp

已编辑的网址:

转到https://technet.microsoft.com/en-us/library/ee156605.aspx,因为上面的链接不再存在。

答案 16 :(得分:0)

感谢this answer:运行Symfony Process Component是运行its documentation的理想工具,它基于proc_*函数,但是使用起来更容易。有关更多信息,请参见https://dotnetfiddle.net

答案 17 :(得分:0)

如果您希望通过PHP执行后台进程,请将命令的输出通过管道传递到/dev/null并将&添加到命令末尾。

exec("bg_process > /dev/null &");

请注意,您不能不能使用$output的{​​{1}}参数,否则PHP将挂起(可能直到该过程完成)。

答案 18 :(得分:-10)

我知道这是一个有100年历史的帖子,但无论如何,认为这可能对某人有用。您可以在页面上的某个位置放置一个不可见的图像,指向需要在后台运行的URL,如下所示:

<img src="run-in-background.php" border="0" alt="" width="1" height="1" />