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 时才有意义)。只有第一次调用此函数返回实际值,后面的调用返回 -1
termsig 整数 导致子进程终止执行的信号的编号(只有当 signaledtrue 时才有意义)。
stopsig 整数 导致子进程停止执行的信号的编号(只有当 stoppedtrue 时才有意义)。

参见

  • proc_open() - 执行命令并打开用于输入/输出的文件指针

添加注释

用户贡献注释 10 个注释

Mark Seecof
14 年前
在 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 *用您的命令替换自己*,而无需启动另一个进程(从技术上讲,在不先 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 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 (错误)
damien at cyg dot net
17 年前
或者,如果您使用 proc_open 调用后续的 PHP 脚本,您可以让该进程在其输出中回显其自身的实际 PID。
此外,如果您通过 Linux 上的 /proc 文件系统,您可以读取 /proc/12345,其中 12345 是 proc_get_status 返回的 pid(/bin/sh 实例的 pid),它将列出其子进程。
andy dot shellam at mailnetwork dot co dot uk
17 年前
进一步说明我之前的说明,我发现返回的 PID 是运行实际请求命令的 shell(/bin/sh)的 PID。

我已将其作为错误 #41003 报告。
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。
lytithwyn at gmail dot com
15 年前
过去一段时间我尝试使用 proc_open 杀死外部命令遇到了很多问题。

其他人建议使用 ps 查找 proc_get_status 返回的 pid 的子进程,但在我的系统上这不起作用。我使用的是 php-5.2.5 和 apache-2.0.59 在 Linux 内核 2.6.21 上,我使用 proc_open 启动的进程最终由 init(pid 1)拥有,而不是由 proc_get_status 返回的 pid 拥有。

但是,我确实注意到进程的 pid 始终高于 proc_get_status pid 并且非常接近。使用这些信息,我编写了一个小函数,它接受命令名称、要搜索的起始 pid(应该是 proc_get_status pid),以及可选的搜索限制作为参数。它将使用 ps 列出由 apache 拥有的进程(您可能需要为您的系统更改此用户名),并搜索指定的命令。限制指示要在起始 pid 之上搜索多远。这将有助于在命令可能已经退出时,您不想杀死与您正在使用的会话不同的会话中的进程。

代码如下:

<?php
function findCommandPID($command, $startpid, $limit = 3)
{
$ps = `ps -u apache --sort=pid -o comm= -o pid=`;
$ps_lines = explode("\n", $ps);

$pattern = "/(\S{1,})(\s{1,})(\d{1,})/";

foreach(
$ps_lines as $line)
{
if(
preg_match($pattern, $line, $matches))
{
// 这将我们限制在父进程的 $limit pid 内找到命令;
// 例如,如果 ppid = 245,limit = 3,我们不会搜索超过 248
if($matches[3] > $startpid + $limit)
break;

// 尝试匹配 ps 行,其中命令与我们的搜索匹配
// 在高于我们父进程的 pid 上
if($matches[1] == $command && $matches[3] > $startpid)
return
$matches[3];
}
}

return
false;
}
?>
To Top