2024 年日本 PHP 研討會

proc_get_status

(PHP 5, PHP 7, PHP 8)

proc_get_status取得由 proc_open() 開啟的處理程序相關資訊

說明

proc_get_status(資源 $process): 陣列

proc_get_status() 會擷取使用 proc_open() 開啟的處理程序之相關資料。

參數

process

將被評估的 proc_open() 資源

回傳值

一個包含收集到的資訊的 陣列。此陣列包含以下元素:

元素類型說明
command 字串 傳遞給 proc_open() 的命令字串。
pid 整數 行程 ID
running 布林值 如果行程仍在執行,則為 true,如果已終止,則為 false
signaled 布林值 如果子行程因未捕獲的訊號而終止,則為 true。在 Windows 上永遠設為 false
stopped 布林值 如果子行程已被訊號停止,則為 true。在 Windows 上永遠設為 false
exitcode 整數 行程返回的退出碼(僅在 runningfalse 時才有意義)。在 PHP 8.3.0 之前,只有第一次呼叫此函式會返回實際值,後續呼叫會返回 -1
cached 布林值 從 PHP 8.3.0 開始,當退出碼被快取時,此值為 true。快取是必要的,以確保退出碼不會被後續對行程 API 的呼叫丟失。
termsig 整數 導致子行程終止執行的訊號編號(僅在 signaledtrue 時才有意義)。
stopsig 整數 導致子行程停止執行的訊號編號(僅在 stoppedtrue 時才有意義)。

更新日誌

版本 說明
8.3.0 已將 "cached" 項目添加到返回的陣列中。在 PHP 8.3.0 之前,只有第一次呼叫會返回實際的退出碼。"cached" 項目表示退出碼已被快取。

參見

  • proc_open() - 執行命令並開啟輸入/輸出的檔案指標

新增註解

使用者貢獻的註解 9 則註解

Mark Seecof
15 年前
在 Unix/Linux 上,如果您稍微更改傳遞給 proc_open() 的命令列,則 proc_get_status() 將會提供您子行程的實際行程 ID (pid)。

假設您希望執行外部命令 /usr/bin/compress 來建立一個 BSD foo.Z 檔案。您可以呼叫 proc_open("exec /usr/bin/compress /tmp/foo",...),而不是 proc_open("/usr/bin/compress /tmp/foo",...),然後 proc_get_status()['pid'] 將會是 /usr/bin/compress 的實際 pid。

原因?因為 `proc_open()` 在 Unix/Linux 系統上的實際運作方式是啟動 "/bin/sh -c 使用者指令 使用者參數...",例如 "/bin/sh -c /usr/bin/compress /tmp/foo"。[註 1] 這表示通常您的指令是 shell 的子程序,因此您使用 `proc_get_status()` 取得的 pid 是 shell 的 pid(PHP 的子程序),而您必須想辦法找到指令的 pid(PHP 的孫程序)。但如果您在指令前面加上 "exec",您就是告訴 shell 以您的指令 *取代自身* 而不啟動另一個程序(技術上來說,就是在不先 fork 的情況下執行您的指令)。這表示您的指令將繼承 shell 的 pid,也就是 `proc_get_status()` 返回的 pid。

因此,如果您想要執行指令的實際程序 pid,只需在 `proc_open()` 指令參數前面加上 "exec ",然後使用 `proc_get_status()` 擷取 pid。

這也讓 `proc_terminate()` 和 `proc_close()` 的運作方式更符合您的預期,因為它們會影響實際執行指令的程序(這將是子程序而不是孫程序)。

[註 1] 我猜 PHP 開發者希望 shell 展開路徑/檔名中的萬用字元。
php dot net at crazedsanity dot com
15 年前
澄清一下,「exitcode」只在程序結束後「第一次呼叫」時有效。

如果您有一個方法會輪詢衍生的程序狀態,您 *必須* 使用相同的方法擷取 exitcode:如果在程序結束後第二次呼叫該方法(在意識到 pid 已失效之後),且該方法尚未快取 exitcode,它將收到上述的 -1。
Lachlan Mulcahy
13 年前
值得注意的是,`proc_get_status` 將持續顯示您衍生的程序正在執行(因為它的確在執行!),直到該程序將所有想寫入的內容都寫入 STDOUT 和 STDERR 串流為止。

PHP 似乎使用緩衝區來執行此操作,因此衍生的程序可以立即取得其寫入呼叫的返回結果。

但是,一旦此緩衝區已滿,寫入呼叫將會阻塞,直到您從串流/管道讀出一些資訊為止。

這可以透過許多方式呈現,但通常被呼叫的程序仍會在執行中,只是沒有執行任何操作,因為它在等待將更多內容寫入 STDERR 或 STDOUT(取決於哪個串流緩衝區已滿)而阻塞。

要解決此問題,您應該在檢查 `proc_get_status` 的 `running` 元素的迴圈中,包含在相關管道的 `stream_get_contents` 呼叫。

我通常使用 `stream_set_blocking($pipies[2], 0)` 之類的呼叫,以確保如果串流中沒有資料,`stream_get_contents` 呼叫不會阻塞。

這個問題困擾了我一段時間,希望它能幫助到其他人!
webmaster at rouen dot fr
16 年前
以下函式接受一個 shell 指令陣列並執行它們。它能夠同時執行最多 $nb_max_process 個指令。一旦一個程序終止,就會執行另一個程序。如果您想在多處理器或多核心環境中批次處理指令,這非常有用。

以下範例嘗試將命令列上提交的 SVG 檔案清單轉換為 PNG(使用 Inkscape)。

(這方法雖然粗糙但對我來說很有效)

#!/usr/bin/php
<?php
function pool_execute($commandes,$nb_max_process) {
$pool=array();
for(
$i=0;$i<$nb_max_process;$i++) {
$pool[$i]=FALSE;
}

while(
count($commandes)>0) {
$commande=array_shift($commandes);

$commande_lancee=FALSE;
while(
$commande_lancee==FALSE) {
usleep(50000);

for(
$i=0;$i<$nb_max_process and $commande_lancee==FALSE;$i++) {
if(
$pool[$i]===FALSE) {
$pool[$i]=proc_open($commande,array(),$foo);
$commande_lancee=TRUE;
} else {
$etat=proc_get_status($pool[$i]);
if(
$etat['running']==FALSE) {
proc_close($pool[$i]);
$pool[$i]=proc_open($commande,array(),$foo);
$commande_lancee=TRUE;
}
}
}
}
}
}

$fichiers=$argv;
array_shift($fichiers);
$commandes=array();
foreach(
$fichiers as $fichier) {
$entree=$fichier;
$sortie=basename($fichier,'.svg').".png";
$commandes[]='inkscape --file='.escapeshellarg($entree).' --export-area-canvas --export-png='.escapeshellarg($sortie);
}

pool_execute($commandes,4);
strrev xc.noxeh@ellij
16 年前
你不能依賴 pid+1 的方式。
您可以在命令字串前加上 exec,這會將 /bin/sh 腳本替換成您真正想要執行的程式(僅在您沒有執行「危險操作」時使用,例如管道、輸出重定向、多個命令;但如果您知道它們如何運作,則可放心使用)。
如果您加上 exec 前綴,/bin/sh 程序將只會啟動您的程序,且 PID 將會相同。
marco.marsala@live.it
1 年前
如果使用 proc_open 啟動 GNU screen,後續的 proc_get_status 總是(錯誤地)返回 running = false

$descriptorspec = array(
0 => array("pipe", "r"), // 標準輸入 (stdin)
1 => array("pipe", "w"), // 標準輸出 (stdout)
2 => array("pipe", "w") // 標準錯誤輸出 (stderr)
);
$p = proc_open('screen ...', $descriptorspec, $pipes);
var_dump(proc_get_status($p)['running']); // false (錯誤)
damien@cyg.net
17 年前
或者,如果您使用 proc_open 呼叫後續的 PHP 腳本,您可以讓該程序在其輸出中顯示其自身的實際 PID。
此外,如果您在 Linux 上瀏覽 /proc 檔案系統,您可以讀取 /proc/12345,其中 12345 是 proc_get_status 返回的 PID(/bin/sh 實例的 PID),它會在其中列出其子程序。
andy.shellam@mailnetwork.co.uk
17 年前
關於我之前的說明,我發現返回的 PID 是 shell (/bin/sh) 的 PID,它會接著執行所請求的實際命令。

我已將此問題回報為錯誤 #41003。
andy.shellam@mailnetwork.co.uk
17 年前
與樓上相同,我的環境是 FreeBSD 6.1,PHP 5.2.1。

為了獲得用於 posix_kill 的正確 PID,我必須將 proc_get_status 返回的 PID 加 1。
To Top