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

时间:2011-12-08 01:15:04

标签: php process child-process

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

我想用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']为false,但是子进程仍在运行。如何实际等待所有孩子的过程?

第二个问题是我没有查看proc_terminate,但我也观察到proc_terminate可能无法正常工作。是因为$ process是bash而不是真正的python进程?也可能是因为我终止的只是父主进程而不是子进程。

任何人都建议我上面的代码是否是健壮的PHP代码?有没有更好的方法来解决这个问题?

感谢。

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

更多信息,

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

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

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

3 个答案:

答案 0 :(得分:2)

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

小心:等待其他进程可能会因超出max_execution_time而导致脚本被终止。它可能也不适合您的Web服务器执行单元的内部管理。

更新:您似乎想要启动进程X,然后等待处理X本身启动的所有进程在继续之前退出。我很遗憾成为坏消息的承担者,但PHP并不适用于这类工作,而你所追求的目标不可能通过PHP库存来实现。当然可以用C语言编写自定义扩展来公开此功能,但据我所知,没有公开的此类扩展。

答案 1 :(得分:2)

proc_get_status可以获得子进程的pid

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

然后您可以使用pcntl_waitpid等待子进程退出

pcntl_waitpid($pid, $child_status);

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

请参阅PCNTL Functions

更新:如果您想在超时后终止子进程

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

function term_proc() {
   // terminate your all child process
}

pcntl_signal(SIGALARM, term_proc);

第二:使用pcntl_alarm在秒后发送警报信号

pcntl_alarm($seconds)

对于第二个问题,proc_terminate无论什么过程被打开都会有效。

答案 2 :(得分:1)

父进程很难跟踪所有的后代。只会通知直接儿童死亡的过程。您可以采取一些不同的机制来尝试做您想做的事情,但最好是重新编写您的应用程序而不关心您的流程的孙子。

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

    虽然您可以自己使用cgroups,但我在cgroupssystemd或其他工具时尝试使用libvirt进行流程管理的后果并未了解多少可能在做类似的事情。 (这是我的无知,而不是cgroups的限制 - 也许它处理得很漂亮。我喜欢做梦。)

  • 在您的流程中打开pipe(2),并将文件描述符中创建的pipe(7)的读取结尾提供给子进程,该文件描述符的大小不会“意外”使用。 (dup2(fd[0], 20)可能很好。)确保您的其他程序不会关闭意外的文件描述符。在写入端设置O_NONBLOCK标志(fd[1])使用fcntl(2)的{​​{1}}命令。

    定期尝试将一个字节写入管道。最终,您将获得F_SETFL错误返回,表明读取结束已被所有孩子关闭或者您将收到错误,并将EPIPE设置为errno,这意味着管道是完整并且还有一个孩子正在跑步。