PHP는 백그라운드 프로세스를 실행


259

사용자 조치에 따라 디렉토리 사본을 실행해야하지만 디렉토리가 상당히 크기 때문에 사용자가 사본을 완료하는 데 걸리는 시간을 인식하지 않고 그러한 조치를 수행 할 수 있기를 원합니다.

어떤 제안이라도 대단히 감사하겠습니다.


3
stackoverflow.com/questions/5367261/… 창에서이 작업을 수행하는 방법을 설명합니다
shealtiel

답변:


365

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

2
암호를 묻지 않고 sudo 설정이 실행됩니까? 사용자 입력이 필요한 명령은 작동하지 않습니다.
Mark Biek

17
stackoverflow.com/questions/5367261/… Windows에서 동일한 작업을 수행하는 방법을 설명합니다
shealtiel

13
나는 이것을 사용했지만 약간 업데이트하여 PID를 파일에 쓸 필요가 없었습니다. 그래서 나는이 형식을 사용합니다 : <code> exec (sprintf ( "$ s> $ s 2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </ code> 결과 프로세스 PID는 $ pidArr [0]
Kaiesh

3
@Kaiesh : "echo $!"이어야합니다.
brunobg

8
Kaiesh는 '%'대신 '$'로 오타를 작성했습니다. 수정 된 버전 : exec (sprintf ( "% s> % s 2> & 1 & echo $!", $ cmd, $ outputfile), $ pidArr)
Yuri Gor

23

프로세스를 편리한 언어 (php / bash / perl / etc)로 서버 측 스크립트로 작성한 다음 PHP 스크립트의 프로세스 제어 기능에서 호출하십시오.

이 함수는 표준 io가 출력 스트림으로 사용되는지 여부를 감지하고 그렇다면 해당 반환 값을 설정합니다.

proc_close( proc_open( "./command --foo=1 &", array(), $foo ) );

"sleep 25s"를 명령으로 사용하여 명령 줄에서이를 빠르게 테스트했으며 매력처럼 작동했습니다.

( 답변은 여기에 있습니다 )


3
이것이 내가 centos에서 작동하도록 할 수있는 유일한 방법입니다. 감사합니다!
Deac Karns

감사 ! PHP와 BASH가 호환되지 않습니다. 최신 시스템을 실행하면 system () 또는 exec ()를 사용하여 백그라운드에서 실행되지 않습니다. 리디렉션하더라도 파일 설명자가 중단 된 것처럼 보입니다. 귀하의 방법이 효과가있었습니다. 또 다른 대안은 PHP에 대한 오래된 bash는 바이너리를 사용하는 것입니다,하지만 그건 견과류입니다

22

이것을 명령에 추가하려고 할 수 있습니다

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

예.

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

> / dev / null 2> / dev / null &
wlf를 의미했습니다.

그것은 나를 위해 일하고, 나는 makefile을 서버 사이트에서 무언가를 할 것입니다, 감사합니다! (예 : make, make restart)
Chu-Siang Lai

이것은 허용되는 답변보다 훨씬 간단합니다! (프로세스가 여전히 실행 중인지 확인하는 것과 같이 더 복잡한 것은 필요하지 않습니다.)
Sean the Bean

18

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에 부여하십시오.

읽기 및 실행 C : \ Windows \ System32 \ cmd.exe에 IUSR 권한 부여

웹 브라우저에서 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가 표시되고 현재 타임 스탬프가 포함 된 다음 텍스트가 포함되어야합니다.

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

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

7

다음은 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명령은 처음 인용 된 매개 변수를 창 제목으로 해석합니다.


6

글쎄, 나는 조금 더 빠르고 사용하기 쉬운 버전을 발견했다.

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

작동합니다.


@ Alpha.Dev 리눅스에서만 테스트했는데 다른 시스템에서 작동하는지 모르겠습니다.
Morfidon

4

별도의 프로세스를 분기 한 후 백그라운드에서 사본을 실행할 수 있습니까? PHP를 사용한 지 오래 되었지만 pcntl-fork 함수 는 유망한 것으로 보입니다.


이것은 질문에 대한 답변을 제공하지 않습니다. 저자에게 비평을하거나 설명을 요청하려면 게시물 아래에 의견을 남겨주십시오.
Rizier123

13
고마워-댓글은 '08에 존재하지 않았지만 명심할 것입니다 :)
Matt Sheppard

4

백그라운드에서 프로그램을 실행하려면이 기능을 사용하십시오. 크로스 플랫폼이며 완벽하게 사용자 정의 할 수 있습니다.

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

3

Resque 와 같은 대기열 시스템을 사용해 볼 수 있습니다. . 그런 다음 정보를 처리하고 "프로세싱"이미지를 사용하여 매우 빠르게 작업을 생성 할 수 있습니다. 이 방법을 사용하면 언제 완료되는지 알 수 없습니다.

이 솔루션은 프론트 머신이 과도한 작업을 수행하지 않기 위해 사용자 요청을 처리 할 수있는 대규모 애플리케이션을위한 것입니다. 따라서 파일 및 폴더와 같은 실제 데이터와 함께 작동하거나 작동하지 않을 수 있지만 더 복잡한 논리 또는 기타 비동기 작업 (예 : 새 등록 메일)을 처리하는 데 유용하고 확장 성이 좋습니다.


2

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


1

백그라운드 프로세스를 시작하는 대신 트리거 파일을 작성하고 cron 또는 autosys와 같은 스케줄러가 주기적으로 트리거 파일을 찾아서 실행하는 스크립트를 실행하는 것은 어떻습니까? 트리거에는 명령이나 원시 명령이 포함될 수 있습니다 (더 나은 방법은 쉘 스크립트로 만듭니다).



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가 Fast CGI 모듈로 실행되는 경우에만 작동합니다 (그렇습니까?!)


0

PHP 스크립팅은 다른 데스크탑 응용 프로그램 개발 언어와 다릅니다. 데스크톱 응용 프로그램 언어에서는 백그라운드 프로세스를 실행하기 위해 데몬 스레드를 설정할 수 있지만 PHP에서는 사용자가 페이지를 요청할 때 프로세스가 발생합니다. 그러나 PHP 스크립트가 실행하는 서버의 크론 작업 기능을 사용하여 백그라운드 작업을 설정할 수 있습니다.


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

백그라운드에서 보이지 않는 쉘 명령을 시작하십시오.

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

수정 된 URL :

위의 링크가 더 이상 존재하지 않으므로 https://technet.microsoft.com/en-us/library/ee156605.aspx로 이동 하십시오 .


1
이 코드를 실행하려면 어떻게해야합니까? 나는Fatal error: Class 'COM' not found
Alph.Dev

MSDN에 대한 Ps 링크가 끊어짐
Alph.Dev

@ Alph.Dev : msdn 문서가 일시적으로 제거 된 것 같습니다. WScript.Shell 사용 방법에 대한 더 많은 예제와 설명이 포함되어 있습니다.
Jimmy Ilenloa

@ Alph.Dev : COM 클래스를 찾을 수 없다면, 그것은 또 다른 것입니다. 구글에서 확인하십시오. 이 php.net/manual/en/com.installation.php를
Jimmy Ilenloa

@ Alph.Dev Run () 함수에 대한 업데이트 된 정보는 technet.microsoft.com/en-us/library/ee156605.aspx참조하십시오
Jimmy Ilenloa

-12

나는 그것이 100 살짜리 게시물이라는 것을 알고 있지만 어쨌든 누군가에게 유용 할 것이라고 생각했습니다. 다음과 같이 백그라운드에서 실행해야하는 URL을 가리키는 페이지 어딘가에 보이지 않는 이미지를 넣을 수 있습니다.

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


12
정말 나쁜 해결책. 사용자가 페이지를 떠나면 프로세스가 중단됩니다.
Sergey P. 일명 azure

3
"보이지 않는 이미지"및 해당 링크는 페이지 소스 코드에 노출됩니다.
tony gil
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.