PHP 只有在 cli 中 运行 时才能终止进程

PHP only able to kill process when run in cli

我有这个 php 脚本“start_stream.php”,它使用 proc_open() 启动进程 (ffmpeg),然后保持循环检查保持活动信号.

一旦 keep-alive 信号太旧,php 脚本将使用此命令通过其 pID 终止进程:

$output = system('taskkill /F /PID '.$this->pID);
echo 'TASKKILL OUTPUT: ';
var_dump($output);

输出总是这样:

TASKKILL OUTPUT: string(55) "SUCCESS: The process with PID 2272 has been terminated."
    

但在某些情况下,进程不会终止,但会保持 运行ning。但我总是收到成功消息。

如果我 运行 来自 cmd (windows) 的 start_stream.php 脚本使用此命令:

php start_stream.php --stream [STREAM-NAME] --url [STREAM-URL]

然后进程将按预期正常终止。

但由于这是一个 Web 应用程序,我需要从另一个 php 脚本调用 start_stream.php 脚本。我这样做:

//Set the command for streaming
$command = 'php start_stream.php --stream '.$stream.' --url '.$url;
//Add the environment variables for shell execution
putenv('PATH=' . $_SERVER['PATH']);
//Set the working directory to streaming directory
chdir("..\streaming");
    
//Start the streaming process
define('STDIN',fopen("php://stdin","r"));
define('STDOUT',fopen("stdout-".$stream,"w"));
$descriptorspec = [STDIN, STDOUT, STDOUT];
$process = proc_open($command, $descriptorspec, $pipes);

当我这样做时,我仍然得到 SUCCESS 输出,说明进程已终止,但进程在后台保持 运行ning。

它可以用这个来终止进程:

shell_exec('taskkill /IM "ffmpeg.exe" /F');

但我不能使用它,因为它会杀死所有 ffmpeg 进程,我需要 运行 并发进程,只杀死相关进程。

正如我之前所说,如果我在命令行中使用以下命令,脚本 运行 会按预期终止:

php start_stream.php --stream [STREAM-NAME] --url [STREAM-URL]

但是如果我像这样使用 php.exe 而不是 php:

php.exe start_stream.php --stream [STREAM-NAME] --url [STREAM-URL]

我会遇到同样的问题,ffmpeg 进程没有终止,但仍然给出成功消息。

我也在 start_stream.php 脚本中使用 proc_terminate() ,如下所示:

proc_terminate($this->process);

但这也给了我一个“进程终止”状态响应,而进程没有停止。

进程的 pID 是 cmd 实例的 pID 运行ning 进程,而不是 ffmpeg 进程本身。当我调用此进程的终止时,cmd 进程在每个实例中都会被终止。 在不使用 php.exe 的情况下从 cmd 调用 php 进程时,ffmpeg 进程与 cmd 进程一起被杀死。使用php.exe时,只有cmd进程被杀死,而ffmpeg进程保持运行ning.

有没有办法确保 ffmpeg 进程与 cmd 进程一起被杀死,或者有什么方法可以在我需要的上下文中获取 ffmpeg 进程本身的 pID?

虽然我能够解决这个问题,但我仍然不确定执行上下文在从 cmd 调用 start_stream.php 和从另一个 php 脚本调用之间发生变化的原因和方式。

当 运行 使用 php 的 proc_open() 的命令时,pID returned 将默认为 shell 执行命令。因此,如果我们通过 shell 启动一个进程,我们将获得执行该进程的 shell 的 pID,而不是子进程本身的 pID。

在某些情况下,终止shell也会终止子进程,但在其他情况下,shell将终止,而子进程保持运行。

如果我们将 bypass_shell = TRUE 传递给 proc_open,php 将 return 子进程的 pID 而不会创建新的 shell 处理以包含它。

下面的例子将return目标进程pID:

$command = "ffmpeg do something";
$descriptorspec = [STDIN, STDOUT, STDOUT];
$otherOptions = ['bypass_shell' => TRUE];
$this->process = proc_open( $command, $descriptorspec, [], NULL, NULL, $otherOptions);