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 条注释

47
Mark Seecof
15 年前
在 Unix/Linux 上,如果您稍微更改传递给 proc_open() 的命令行,则 proc_get_status() 将为您提供子进程的实际进程 ID (pid)。

假设您希望运行外部命令 /usr/bin/compress 来创建一个 BSD foo.Z 文件。与其使用 proc_open("/usr/bin/compress /tmp/foo",...),您可以调用 proc_open("exec /usr/bin/compress /tmp/foo",...),然后 proc_get_status()['pid'] 将是 /usr/bin/compress 的实际 pid。

为什么?因为 proc_open() 在 Unix/Linux 上的实际工作方式是启动 "/bin/sh -c usercmd userargs...",例如 "/bin/sh -c /usr/bin/compress /tmp/foo"。[注 1] 这意味着通常您的命令是 shell 的子进程,因此您使用 proc_get_status() 获取的 pid 是 shell 的 pid(PHP 的子进程),您必须四处摸索才能找到命令的 pid(PHP 的孙进程)。但是,如果您在命令前面加上 "exec",您告诉 shell *用您的命令替换自身*,而无需启动另一个进程(技术上讲,是在不先分叉的情况下执行您的命令)。这意味着您的命令将继承 shell 的 pid,这就是 proc_get_status() 返回的 pid。

因此,如果您想要运行您的命令的进程的实际 pid,只需在您的 proc_open() 命令参数前面加上 "exec ",然后使用 proc_get_status() 获取 pid。

这也会使 proc_terminate() 和 proc_close() 按照您可能更喜欢的方式工作,因为它们将影响运行您命令的实际进程(它将是子进程而不是孙进程)。

[注 1] 我猜 PHP 开发人员希望 shell 展开路径/文件名中的通配符。
12
php dot net at crazedsanity dot com
15 年前
澄清一下,"exitcode" 只有在进程退出后 *第一次调用* 时才有效。

如果您有一个方法来轮询生成的进程以获取其状态,您 *必须* 让该方法捕获 exitcode:如果该方法第二次被调用(在意识到 pid 已死亡后),并且它没有缓存该 exitcode,它将收到提到的 -1。
10
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 调用在流中没有数据时不会阻塞。

这个让我困惑了一段时间,所以希望它能帮助到其他人!
3
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);
1
strrev xc.noxeh@ellij
16 年前
你不能依赖 pid+1。
你可以在命令字符串前加 exec,这将用你想要执行的真实命令替换 /bin/sh 脚本(仅当你不做“危险操作”例如管道、输出重定向、多条命令时使用,但是如果你知道它们的工作原理,尽管去做)。
如果你加了 exec 前缀,/bin/sh 进程只会启动你的进程,并且 PID 将保持不变。
0
marco dot marsala at live dot 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 (错误)
0
damien at cyg dot net
17年前
或者,如果你使用 proc_open 调用后续的 php 脚本,你可以让该进程在其输出中回显其自己的实际 PID。
此外,如果你在 Linux 上遍历 /proc 文件系统,你可以读取 /proc/12345,其中 12345 是 proc_get_status 返回的 pid(/bin/sh 实例的 pid),它将列出其子进程。
-1
andy dot shellam at mailnetwork dot co dot uk
17年前
补充我之前的说明,我发现返回的 PID 是 shell (/bin/sh) 的 PID,然后它运行请求的实际命令。

我已经将其作为一个错误 #41003 提交。
-3
andy dot shellam at mailnetwork dot co dot uk
17年前
对于上面的发帖者,我在 FreeBSD 6.1、PHP 5.2.1 上也是如此。

为了获得 posix_kill 正确使用的 PID,我必须将 proc_get_status 返回的 PID 加 1。
To Top