php выполняет фоновый процесс

Мне нужно выполнить копию каталога при действии пользователя, но каталоги довольно велики, поэтому я хотел бы иметь возможность выполнить такое действие без того, чтобы пользователь знал о времени, которое требуется для завершения копирования.

Любые предложения будут высоко ценится.

Предполагая, что это работает на Linux-машине, я всегда обрабатывал это следующим образом:

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

Это запускает команду $cmd , перенаправляет вывод команды в выходной файл $outputfile и записывает идентификатор процесса в $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; } 

Запишите процесс как серверный скрипт на любом языке (php / bash / perl / etc), а затем вызовите его из функций управления процессом в вашем php-скрипте.

Функция, вероятно, обнаруживает, что в качестве выходного потока используется стандартный io, и если он будет тогда, он установит возвращаемое значение. Если он не заканчивается

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

Я быстро проверил это из командной строки, используя «sleep 25s» в качестве команды, и это сработало как шарм.

( Ответ найден здесь )

Я просто хотел бы добавить очень простой пример для тестирования этой функции в Windows:

Создайте следующие два файла и сохраните их в веб-каталоге:

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 из веб-браузера

В браузере с текущими временными метками и локальным ресурсом # в выходном массиве должно быть отображено следующее:

 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 в том же каталоге, в котором были сохранены указанные выше файлы, и он должен содержать следующий текст w / current timestamps:

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

Возможно, вы захотите добавить это в свою команду

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

например.

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

Если вам нужно просто сделать что-то в фоновом режиме без страницы PHP, ожидающей его завершения, вы можете использовать другой (фоновый) PHP-скрипт, который «вызывается» с помощью команды wget. Этот фоновый PHP-скрипт будет выполняться с привилегиями, конечно же, как и любой другой PHP-скрипт в вашей системе.

Ниже приведен пример использования Windows с помощью wget из пакетов gnuwin32.

Фоновый код (файл test-proc-bg.php) как exmple …

 sleep(5); // some delay file_put_contents('test.txt', date('Ymd/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 

Ну, я нашел немного более быструю и удобную версию для использования

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

и это работает.

Вы можете попробовать систему массового обслуживания, такую ​​как Resque . Затем вы можете сгенерировать задание, которое обрабатывает информацию и довольно быстро возвращается с «обрабатывающим» изображением. При таком подходе вы не будете знать, когда это будет закончено.

Это решение предназначено для приложений большего масштаба, где вы не хотите, чтобы ваши передние машины выполняли тяжелый подъем, поэтому они могут обрабатывать запросы пользователей. Поэтому он может работать или не работать с физическими данными, такими как файлы и папки, но для обработки более сложной логики или других асинхронных задач (т.е. новых писем с регистрацией) приятно иметь и очень масштабируемо.

Вот функция запуска фонового процесса в 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: В окнах не используйте параметр /B как предлагается в другом месте. Это заставляет процесс запускать то же окно консоли, что и сама команда start , в результате процесс обрабатывается синхронно. Чтобы запустить процесс в отдельном потоке (асинхронно), не используйте /B

Примечание 2: Пустые двойные кавычки после start "" требуются, если команда является цитируемым путем. Команда start интерпретирует первый цитируемый параметр как заголовок окна.

Можете ли вы организовать разветвление отдельного процесса, а затем запустить копию в фоновом режиме? Прошло некоторое время с тех пор, как я сделал любой PHP, но функция pcntl-fork выглядит многообещающей.

Вместо того, чтобы инициировать фоновый процесс, как насчет создания триггерного файла и с планировщиком, например cron или autosys, периодически выполняется сценарий, который ищет и действует на файлы триггеров? Триггеры могут содержать инструкции или даже необработанные команды (лучше всего, просто сделайте это скриптом оболочки).

Если вы используете PHP, гораздо проще сделать это с помощью pcntl_fork:

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

Я сильно использую 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.

Сообщение $ не будет отображаться в любое время. И вы можете зарегистрировать столько закрытий, сколько захотите, но позаботьтесь о времени выполнения скрипта. Это будет работать, только если PHP работает как быстрый модуль CGI (это было правильно ?!)

Используйте эту функцию для запуска вашей программы в фоновом режиме. Это кросс-платформенный и полностью настраиваемый.

 <?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'); 

Поэтому вы должны перенаправить stderr / stdin в действительный файл или ваша команда должна ожидать, что это не дескриптор stderr / stdout.

Примечания для пользователей * nix:

1) Используйте команду exec shell, если вы хотите получить фактический PID:

 $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"); 

PHP-скриптинг не похож на другой язык разработки настольных приложений. В настольных языках приложений мы можем установить потоки демона для запуска фонового процесса, но в PHP процесс возникает, когда пользователь запрашивает страницу. Однако можно установить фоновое задание, используя функциональные возможности сервера cron, выполняемые скриптом php.

Для тех из нас, кто использует 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); ?> 

запустите команду оболочки, невидимую в фоновом режиме:

 <?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

Рабочее решение для Windows и Linux. Узнайте больше о моей странице github .

 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; } } } 

Я знаю, что это 100-летняя должность, но в любом случае, подумал, что это может быть полезно кому-то. Вы можете поместить невидимое изображение где-нибудь на странице, указывающую на URL-адрес, который должен запускаться в фоновом режиме, например:

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