2024 日本 PHP 研討會

proc_terminate

(PHP 5, PHP 7, PHP 8)

proc_terminate終止由 proc_open 開啟的行程

說明

proc_terminate(資源 $process, int $signal = 15): bool

向使用 proc_open() 建立的 process(行程)發送終止訊號。proc_terminate() 會立即返回,不會等待行程終止。

proc_terminate() 允許您終止行程並繼續執行其他任務。您可以使用 proc_get_status() 函式輪詢行程(查看它是否已停止)。

參數

process

將被關閉的 proc_open() 資源

signal

此選用參數僅在 POSIX 作業系統上有用;您可以使用 kill(2) 系統呼叫指定要發送給行程的訊號。預設值是 SIGTERM

傳回值

傳回已執行行程的終止狀態。

參見

  • proc_open() - 執行指令並開啟用於輸入/輸出的檔案指標
  • proc_close() - 關閉由 proc_open 開啟的行程並傳回該行程的結束代碼
  • proc_get_status() - 取得由 proc_open 開啟的行程的相關資訊

新增註記

使用者貢獻的註記 4 則註記

csh dot spam at me dot com
11 年前
/bin/sh -c CMD 會 fork sh 然後執行 CMD。
/bin/sh -c exec CMD 不會 fork 且只會執行 CMD。

因此,您可以透過在指令前面加上 "exec bla bla bla" 來擺脫這個 hack。
jerhee at ucsd dot edu
16 年前
http://bugs.php.net/bug.php?id=39992 中所述,proc_terminate() 會讓子行程的子行程繼續執行。在我的應用程式中,這些子行程通常會有無限迴圈,所以我需要一個可靠的方法來終止使用 proc_open() 建立的行程。當我呼叫 proc_terminate() 時,/bin/sh 行程會被終止,但具有無限迴圈的子行程會繼續執行。

在 proc_terminate() 被修復之前,我不建議使用它。我的解決方案是:
1) 呼叫 proc_get_status() 來取得我要終止的行程的父行程 ID (ppid)。
2) 使用 ps 取得所有以該 ppid 作為父行程 ID 的 pid。
3) 使用 posix_kill() 向每個子行程 pid 發送 SIGKILL (9) 訊號。
4) 對行程資源呼叫 proc_close()。

<?php
$descriptorspec
= array(
0 => array('pipe', 'r'), // stdin 是一個子程序將從中讀取的管道
1 => array('pipe', 'w'), // stdout 是一個子程序將寫入的管道
2 => array('pipe', 'w') // stderr 是一個子程序將寫入的管道
);
$process = proc_open('bad_program', $descriptorspec, $pipes);
if( !
is_resource($process)) {
throw new
Exception('bad_program 無法啟動。');
}
//將一些輸入傳遞給程序
fwrite($pipes[0], $lots_of_data);
//關閉 stdin。通過關閉 stdin,程序應該在
//完成輸入處理後退出
fclose($pipes[0]);

//做一些其他的事情... 程序可能仍在運行
//如果我們立即檢查它

$status = proc_get_status($process);
if(
$status['running'] == true) { //程序運行時間過長,將其終止
//關閉所有仍在開啟的管道
fclose($pipes[1]); //stdout
fclose($pipes[2]); //stderr
//取得我們要終止的程序的父進程 ID
$ppid = $status['pid'];
//使用 ps 取得此進程的所有子進程,並終止它們
$pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`);
foreach(
$pids as $pid) {
if(
is_numeric($pid)) {
echo
"正在終止 $pid\n";
posix_kill($pid, 9); // 9 是 SIGKILL 信號
}
}

proc_close($process);
}

?>
v dot denegin at yahoo dot com
10 年前
在 Windows 平台上,`proc_terminate()` 不會終止未處理 kill 信號的子程序。即使您呼叫 xxx.exe 並呼叫 `proc_terminate()`,該程序仍將保持活動狀態。

解決方案是,不要呼叫 `proc_terminate()`,而是呼叫使用者自訂的 `kill()` 函式(已針對 win/unix 最佳化)。
之後,需要關閉所有管道並執行 `proc_close()`。

function kill($pid){
return stripos(php_uname('s'), 'win') > -1 ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid"); // 根據系統執行不同指令來強制終止行程
}

function killall($pids) { // 終止多個行程的函式
$os = stripos(php_uname('s'), 'win') > -1; // 判斷作業系統是否為 Windows
($_ = implode($os ? ' /PID ' : ' ', $pids)) or ($_ = $pids); // 將行程 ID 組合成命令列參數字串
return preg_match('/success|close/', $os ? exec("taskkill /F /T /PID $_") : exec("kill -9 $_")); // 執行終止指令並檢查結果
}

範例

$pstatus = proc_get_status($resource); // 取得行程狀態
$PID = $pstatus['pid']; // 取得行程 ID

// 其他指令

kill($PID); // 使用 kill 函式終止行程,取代 proc_terminate($resource);
fclose($pipes[0]); // 關閉管道
fclose($pipes[1]); // 關閉管道
fclose($pipes[2]); // 關閉管道
proc_close($resource); // 關閉行程資源
smcbride at msn dot com
3 年前
簡短說明,讓大家不用再到處尋找

要取得行程列表或依名稱尋找行程,請使用
$proclist = shell_exec('ps -elF'); // Linux 系統
$proclist = shell_exec('tasklist'); // Windows 系統

之後,您可以使用 PHP 的一般解析函式來取得行程 ID
To Top