使用PHP's proc_open + bypass_shell在后台运行可执行文件并检索正确的PID


Use PHP's proc_open + bypass_shell to run an executable in the background AND retrieve the correct PID?

所以,在Windows上的PHP:是否可以同时在后台运行可执行文件检索其PID?我已经推断出可以分别完成这两项任务,但不能同时完成。

进程背景

通过SHELL启动的进程,必须使用'start /B "bg" myprog.exe'命令,并且SHELL进程必须立即关闭。

要做到这一点,许多人使用pclose( popen( ... ) )pclose( popen( 'start /B "bg" myprog.exe', 'r') );,但据我所知,这是不可能检索pid时,使用popen。

因为用popen是不可能得到pid的,我们必须看proc_open。

获取PID

使用proc_open,当且仅当 bypass_shell设置为true时,我们可以检索exe的pid。

如果bypass_shell设置为false(默认),Windows返回SHELL的pid。更多信息请参见:https://bugs.php.net/bug.php?id=41052

问题解释

bypass_shell = true传递给proc_open时,start /B命令失败,因为它跳过SHELL并将命令行参数直接发送给myprog.exe,而myprog.exe不知道如何处理它们。

相反,如果使用bypass_shell = false(默认)和proc_close立即关闭SHELL, myprog.exe在后台运行,就像使用pclose( popen( ... ) )时一样,但是返回不正确的 pid(我们得到SHELL的pid)。

那么,背景+正确pid检索是可能的吗?

如果没有,下一个最好的是什么?我需要这样做的PHP脚本,将部署在共享主机上,所以没有第三方扩展可以安装。我能想到的最好的办法是在后台启动myprog.exe之前和之后对tasklist进行快照,然后交叉分析结果。注意myprog.exe可以并发运行。

如果它有帮助,虽然它不应该有什么不同,myprog.exe实际上是ffmpeg(它安装在大多数共享的网络主机上)。

临时解决方案

// background the process
pclose( popen( 'start /B "bg" ffmpeg.exe', 'r') );
// get the pid using tasklist
exec( 'TASKLIST /NH /FO "CSV" /FI "imagename eq ffmpeg.exe" /FI "cputime eq 00:00:00"', $output );
$output = explode( '","', $output[0] );
$pid = $output[1];

这并不完全是OP的问题的答案,因为它不会在windows服务器上工作,但它将在任何启用exec()的linux服务器上工作得很好,所以它可能会帮助某人;)

$pidfile = 'myPidFile'; //coule be done better with tempnam(), but for this example will do like that ;)
$outputfile = '/dev/null'; //can be any text file instead of /dev/null. output from executable will be saved there
$cmd = 'sleep 30'; // this would normaly take 30 seconds
exec(sprintf("%s > %s 2>&1 & echo $! > %s", $cmd, $outputfile, $pidfile));
$pid = file_get_contents($pidfile);
echo $pid;
//delete pid file if you want
//unlink($pidfile);