如何在PHP中等待进程终止(以及等待相应的子进程)


How to wait for a process to terminate (and also wait for the corresponding child processes) in PHP?

我有一个程序,它通常运行到终止,但在某些情况下,它创建了两个子进程,然后主进程退出。主进程退出后,子进程将继续运行。

我想在PHP中调用这个程序。

最初,我使用以下策略(无效php代码),

$process=proc_open("python xxx.py");
while (1) {
   sleep(5);
   $status = proc_get_status($process);
   if (!$status['running']) {
       exit_loop;
   }
   if (timeout) {
      proc_terminate($process, 9);
      exit_loop;
   }
}

但是我很快发现了这个错误,进程实际上立即退出,$status['running']是假的,但是子进程仍然在运行。如何真正等待所有子进程?

第二个问题是我没有研究proc_terminate,但我也观察到proc_terminate可能不像我预期的那样工作。这是因为$process是bash而不是真正的python进程吗?这也可能是因为我终止的只是父主进程,而不是子进程。

有没有人告诉我,上面的代码是不是健壮的PHP代码?有更好的办法来处理这件事吗?

谢谢。

======================

更多的信息,

  1. 我在shell (CLI)中运行这个程序,与CGI函数无关。所以我不需要担心max_execution_time。

  2. 我想等待proc_open打开的主进程创建的子进程,我将只在程序中创建一个进程。

  3. 我想调用的python程序可能会挂起很长时间,所以我想检查超时并终止它

proc_get_status可以获取子进程的pid

$status = proc_get_status($process);
$pid = $status["pid"]

则可以使用pcntl_waitpid等待子进程退出

pcntl_waitpid($pid, $child_status);

但是你需要用pcntl扩展来编译你的php。

参见PCNTL Functions

UPDATE:如果你想在超时后杀死子进程

首先:在主进程中安装SIGALARM处理程序

function term_proc() {
   // terminate your all child process
}
pcntl_signal(SIGALARM, term_proc);

秒:用pcntl_alarm在秒后发出报警信号

pcntl_alarm($seconds)

对于第二个问题,无论打开什么进程,proc_terminate都可以工作。

如果您不打算以某种方式与生成的进程交互(例如通过重定向其输入或输出),那么您可以使用system而不是proc_opensystem将等待生成的进程完成,所以不需要经常检查它。

小心:等待另一个进程可能会导致您的脚本因超出max_execution_time而终止。它也可能不能很好地与你的web服务器的内部管理执行单元。

Update:似乎你想启动一个进程X,然后等待进程X自己启动的所有进程退出,然后再继续。我很抱歉成为坏消息的承担者,但PHP不是为这种类型的工作而设计的,您所追求的是不可能实现的股票PHP。当然可以用C编写一个自定义扩展来公开此功能,但据我所知,没有这样的扩展公开可用。

父进程很难遵循其所有子进程的 。只有在儿童直接死亡的情况下才会通知程序。您可以采用几种不同的机制来尝试做您想做的事情,但最好是重写您的应用程序,以不关心您的进程的孙子。

  • cgroupssystemd提供了足够的机制来跟踪从单个初始execve(2)调用开始的所有子进程。

    虽然您可以自己使用cgroups,但我还没有了解到试图使用cgroups进行过程管理的后果,而systemdlibvirt或其他工具可能正在做类似的事情。(这是我的无知,而不是cgroups的局限性——也许它处理得很好。我喜欢做梦。

  • 在进程中打开pipe(2),并在文件描述符中为子进程创建pipe(7)的读端,该文件描述符足够高,不会"意外"使用。(dup2(fd[0], 20)可能是好的)确保您的其他程序不会关闭意外的文件描述符。使用fcntl(2)F_SETFL命令设置写端(fd[1])的O_NONBLOCK标志。

    定期尝试将一个字节写入管道。最终,您将获得EPIPE错误返回,指示读端已被所有子进程关闭,或者您将获得errno设置为EAGAIN的错误返回,这意味着管道并且仍然有子进程在运行。