php thực hiện một quá trình nền


259

Tôi cần phải thực thi một bản sao thư mục theo hành động của người dùng, nhưng các thư mục khá lớn, vì vậy tôi muốn có thể thực hiện một hành động như vậy mà không cần người dùng nhận thức được thời gian để bản sao hoàn thành.

Bất kỳ đề xuất sẽ được nhiều đánh giá cao.


3
Stackoverflow.com/questions/5367261/ này giải thích cách thực hiện việc này dưới cửa sổ
shealtiel

Câu trả lời:


365

Giả sử điều này đang chạy trên máy Linux, tôi đã luôn xử lý nó như thế này:

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

Điều này khởi chạy lệnh $cmd, chuyển hướng đầu ra lệnh đến $outputfilevà ghi id quá trình vào $pidfile.

Điều đó cho phép bạn dễ dàng theo dõi quá trình đang làm gì và nếu nó vẫn đang chạy.

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
Là thiết lập sudo để chạy mà không cần nhập mật khẩu? Bất kỳ lệnh nào yêu cầu đầu vào của người dùng sẽ không hoạt động.
Đánh dấu Biek

17
Stackoverflow.com/questions/5367261/ này giải thích cách làm tương tự dưới cửa sổ
shealtiel

13
Tôi đã sử dụng cái này, nhưng đã cập nhật nó một chút để tôi không phải ghi PID vào một tập tin. Vì vậy, tôi sử dụng định dạng này: <code> exec (sprintf ("$ s> $ s 2> & 1 & echo $ 1", $ cmd, $ outputfile), $ pidArr); </ code> Quá trình kết quả PID nằm ở $ pidArr [0]
Kaiesh

3
@Kaiesh: nên là "echo $!"
brunobg

8
Kaiesh đã đánh máy anoter, '$' thay vì '%'. Phiên bản đã sửa: exec (sprintf ("% s>% s 2> & 1 & echo $!", $ Cmd, $ outputfile), $ pidArr)
Yuri Gor

23

Viết quy trình dưới dạng tập lệnh phía máy chủ bằng bất kỳ ngôn ngữ nào (php / bash / perl / etc) là tiện dụng và sau đó gọi nó từ các hàm điều khiển quy trình trong tập lệnh php của bạn.

Hàm có thể phát hiện nếu io tiêu chuẩn được sử dụng làm luồng đầu ra và nếu đúng thì nó sẽ đặt giá trị trả về..nếu không thì nó kết thúc

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

Tôi đã thử nghiệm điều này một cách nhanh chóng từ dòng lệnh bằng cách sử dụng "ngủ 25s" làm lệnh và nó hoạt động như một lá bùa.

( Trả lời tìm thấy ở đây )


3
Đây là cách duy nhất tôi có thể làm cho nó hoạt động trên centos. Cảm ơn bạn!
Deac Karns

Cảm ơn ! Có sự không tương thích giữa PHP và BASH. Nếu bạn chạy một hệ thống mới hơn, nó sẽ không khởi chạy trong nền bằng cách sử dụng system () hoặc exec (). Các mô tả tập tin dường như bị kẹt ngay cả khi bạn chuyển hướng. Phương pháp của bạn đã làm việc. một cách khác là sử dụng một nhị phân bash cũ cho PHP, nhưng đó là điều tồi tệ
John

22

Bạn có thể muốn thử nối nó vào lệnh của bạn

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

ví dụ.

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

Ý tôi là> / dev / null 2> / dev / null &
wlf

Nó hoạt động với tôi và tôi sẽ sử dụng makefile để làm gì đó tại trang web của máy chủ, cảm ơn bạn! (ví dụ: thực hiện, thực hiện khởi động lại)
Chu-Siang Lai

Điều này đơn giản hơn nhiều so với câu trả lời được chấp nhận! (Miễn là bạn không cần bất cứ điều gì phức tạp hơn, như kiểm tra xem quy trình có còn chạy không.)
Sean the Bean

18

Tôi chỉ muốn thêm một ví dụ rất đơn giản để thử nghiệm chức năng này trên Windows:

Tạo hai tệp sau và lưu chúng vào thư mục 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>";
?>

nền.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);
?>

Cấp quyền cho IUSR để ghi vào thư mục mà bạn đã tạo các tệp trên

Cấp quyền cho IUSR để ĐỌC và EXECUTE C: \ Windows \ System32 \ cmd.exe

Nhấn foreground.php từ trình duyệt web

Sau đây sẽ được hiển thị cho trình duyệt với dấu thời gian hiện tại và tài nguyên cục bộ # trong mảng đầu ra:

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

Bạn sẽ thấy testoutput.php trong cùng thư mục với các tệp trên đã được lưu và nó sẽ trống

Bạn sẽ thấy test Processes.php trong cùng thư mục với các tệp trên đã được lưu và nó sẽ chứa văn bản sau với các dấu thời gian hiện tại:

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

10

Nếu bạn chỉ cần làm một cái gì đó trong nền mà không cần trang PHP chờ nó hoàn thành, bạn có thể sử dụng một tập lệnh PHP (nền) khác được "gọi" bằng lệnh wget. Kịch bản PHP nền này sẽ được thực thi với các đặc quyền, tất nhiên, như bất kỳ tập lệnh PHP nào khác trên hệ thống của bạn.

Dưới đây là một ví dụ trên Windows bằng cách sử dụng wget từ các gói gnuwin32.

Mã nền (tệp test-Proc-bg.php) dưới dạng exmple ...

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

Kịch bản nền trước, một ...

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

Bạn phải sử dụng popen / pclose để hoạt động đúng.

Các tùy chọn wget:

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

7

Đây là một chức năng để khởi chạy một quá trình nền trong PHP. Cuối cùng cũng tạo ra một cái thực sự hoạt động trên Windows, sau khi đọc và kiểm tra các cách tiếp cận và tham số khác nhau.

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

Lưu ý 1: Trên các cửa sổ, không sử dụng /Btham số như được đề xuất ở nơi khác. Nó buộc quá trình chạy cùng một cửa sổ giao diện điều khiển như startchính lệnh, dẫn đến quá trình được xử lý đồng bộ. Để chạy quy trình trong một luồng riêng biệt (không đồng bộ), không sử dụng/B .

Lưu ý 2: Dấu ngoặc kép trống sau start ""được yêu cầu nếu lệnh là đường dẫn được trích dẫn. startlệnh diễn giải tham số được trích dẫn đầu tiên là tiêu đề cửa sổ.


6

Vâng, tôi tìm thấy một phiên bản nhanh hơn và dễ sử dụng hơn

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

Và nó hoạt động.


@ Alpha.Dev Tôi chỉ thử nghiệm nó trên linux. Tôi không biết liệu nó có hoạt động trên một sytem khác không.
Morfidon

4

Bạn có thể sắp xếp để tách ra một quy trình riêng biệt, và sau đó chạy bản sao của bạn trong nền không? Đã được một thời gian kể từ khi tôi thực hiện bất kỳ PHP nào, nhưng chức năng pcntl-fork có vẻ đầy hứa hẹn.


Điều này không cung cấp một câu trả lời cho câu hỏi. Để phê bình hoặc yêu cầu làm rõ từ một tác giả, hãy để lại nhận xét bên dưới bài đăng của họ.
Rizier123

13
Cảm ơn - Nhận xét không tồn tại trong '08, nhưng tôi sẽ ghi nhớ điều đó :)
Matt Sheppard

4

Sử dụng chức năng này để chạy chương trình của bạn trong nền. Nó đa nền tảng và hoàn toàn tùy biến.

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

Lưu ý rằng sau khi lệnh bắt đầu, theo mặc định, hàm này sẽ đóng stdin và stdout của tiến trình đang chạy. Bạn có thể chuyển hướng đầu ra quá trình vào một số tệp thông qua các đối số $ redirectStdout và $ redirectStderr.

Lưu ý cho người dùng windows:
Bạn không thể chuyển hướng stdout / stderr nultheo cách sau:

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

Tuy nhiên, bạn có thể làm điều này:

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

Ghi chú cho người dùng * nix:

1) Sử dụng lệnh exec shell nếu bạn muốn có được PID thực tế:

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

2) Sử dụng đối số $ stdin nếu bạn muốn truyền một số dữ liệu vào đầu vào của chương trình:

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

3

Bạn có thể thử một hệ thống xếp hàng như Resque . Sau đó, bạn có thể tạo một công việc, xử lý thông tin và trả lại khá nhanh với hình ảnh "xử lý". Với phương pháp này bạn sẽ không biết khi nào nó kết thúc.

Giải pháp này dành cho các ứng dụng quy mô lớn hơn, nơi bạn không muốn các máy trước của mình thực hiện công việc nặng nhọc, để chúng có thể xử lý các yêu cầu của người dùng. Do đó, nó có thể hoặc không thể hoạt động với dữ liệu vật lý như tệp và thư mục, nhưng để xử lý logic phức tạp hơn hoặc các tác vụ không đồng bộ khác (ví dụ: thư đăng ký mới), thật tuyệt khi có và có khả năng mở rộng.


2

Một giải pháp làm việc cho cả Windows và Linux. Tìm thêm trên trang github của tôi .

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

Thay vì bắt đầu một quá trình nền, vậy còn việc tạo tệp kích hoạt và có một trình lập lịch biểu như cron hoặc autosys định kỳ thực thi một tập lệnh tìm kiếm và hoạt động trên các tệp kích hoạt thì sao? Các trình kích hoạt có thể chứa các hướng dẫn hoặc thậm chí các lệnh thô (tốt hơn hết, chỉ cần làm cho nó thành một tập lệnh shell).



1

Tôi đang sử dụng rất nhiều fast_cgi_finish_request()

Kết hợp với bao đóng và register_shutdown_function ()

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

Sau đó đăng ký đóng cửa này để được thực hiện trước khi tắt máy.

register_shutdown_function($backgroundJob);

Cuối cùng, khi phản hồi được gửi đến máy khách, bạn có thể đóng kết nối với máy khách và tiếp tục làm việc với quy trình PHP:

fast_cgi_finish_request();

Việc đóng cửa sẽ được thực hiện sau fast_cgi_finish_Vquest.

Thông báo $ sẽ không hiển thị bất cứ lúc nào. Và bạn có thể đăng ký bao nhiêu lần đóng như bạn muốn, nhưng hãy quan tâm đến thời gian thực hiện tập lệnh. Điều này sẽ chỉ hoạt động nếu PHP đang chạy như một mô-đun CGI nhanh (đúng không?!)


0

PHP scripting không giống như ngôn ngữ phát triển ứng dụng máy tính để bàn khác. Trong các ngôn ngữ ứng dụng máy tính để bàn, chúng ta có thể đặt các luồng daemon để chạy một quá trình nền nhưng trong PHP, một quá trình xảy ra khi người dùng yêu cầu một trang. Tuy nhiên, có thể đặt công việc nền bằng chức năng công việc định kỳ của máy chủ mà tập lệnh php chạy.


0

Đối với những người trong chúng ta sử dụng Windows , hãy xem điều này:

Tham khảo: http://php.net/manual/en/feft.exec.php#43917

Tôi đã quá vật lộn với việc để một chương trình chạy trong nền trong Windows trong khi tập lệnh tiếp tục thực thi. Phương pháp này không giống như các giải pháp khác cho phép bạn bắt đầu bất kỳ chương trình nào được thu nhỏ, tối đa hóa hoặc hoàn toàn không có cửa sổ. Giải pháp của llbra @ phpbrasil không hoạt động nhưng đôi khi nó tạo ra một cửa sổ không mong muốn trên màn hình nền khi bạn thực sự muốn tác vụ chạy ẩn.

bắt đầu Notepad.exe thu nhỏ trong nền:

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

bắt đầu một lệnh shell vô hình trong nền:

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

bắt đầu tối đa hóa MSPaint và đợi bạn đóng nó trước khi tiếp tục tập lệnh:

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

Để biết thêm thông tin về phương thức Run (), hãy truy cập: http://msdn.microsoft.com/l Library / en-us / script56 / html / wsMthRun.asp

Đã chỉnh sửa URL:

Truy cập https://technet.microsoft.com/en-us/l Library / e156605.aspx thay vì liên kết ở trên không còn tồn tại.


1
Cần phải làm gì để chạy mã này? Tôi nhận đượcFatal error: Class 'COM' not found
Alph.Dev

Liên kết Ps đến MSDN bị hỏng
Alph.Dev

@ Alph.Dev: Tài liệu msDN dường như đã bị xóa tạm thời. Nó chứa nhiều ví dụ và giải thích về cách sử dụng WScript.Shell.
Jimmy Ilenloa

@ Alph.Dev: Đối với lớp COM không được tìm thấy, đó là một điều khác hoàn toàn. Kiểm tra xem trên google. Bạn cũng có thể kiểm tra điều này php.net/manual/en/com.installation.php
Jimmy Ilenloa

@ Alph.Dev vui lòng kiểm tra technet.microsoft.com/en-us/l Library / ese156605.aspx để biết thông tin cập nhật về chức năng Run ()
Jimmy Ilenloa

-12

Tôi biết đó là một bài viết 100 năm tuổi, nhưng dù sao, nghĩ rằng nó có thể hữu ích cho một ai đó. Bạn có thể đặt một hình ảnh vô hình ở đâu đó trên trang chỉ vào url cần chạy trong nền, như thế này:

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


12
Giải pháp thực sự tồi tệ. Nếu người dùng rời khỏi trang thì quá trình sẽ bị gián đoạn.
Serge P. hay còn gọi là azure

3
"hình ảnh bất khả xâm phạm" và liên kết của nó được hiển thị trong mã nguồn trang.
tony gil
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.