【问题标题】:How to wait for a process to terminate (and also wait for the corresponding child processes) in PHP?如何在 PHP 中等待进程终止(也等待相应的子进程)?
【发布时间】:2011-12-08 01:15:03
【问题描述】:

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

我想用 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程序可能会挂很长时间,所以我想检查超时并终止它。

【问题讨论】:

    标签: php process child-process


    【解决方案1】:

    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】:

      如果您不打算以某种方式与生成的进程交互(例如,通过重定向其输入或输出),那么您可以使用 system 而不是 proc_open。 system 将等待生成的进程完成,因此无需每隔一段时间检查一次。

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

      更新:您似乎想启动一个进程 X,然后等待进程 X 本身启动的所有进程退出,然后再继续。我很抱歉成为坏消息的承担者,但是 PHP 不适合这种类型的工作,而您所追求的目标是无法使用普通 PHP 实现的。当然可以用 C 语言编写自定义扩展来公开此功能,但据我所知,没有这样的扩展公开可用。

      【讨论】:

      • 感谢您的回答。我在 shell (CLI) 中运行这个程序,与 CGI 函数无关。所以我不需要担心 max_execution_time。我要等待proc_open打开的主进程创建的子进程,我只会在程序中创建一个进程。最后,使用其他程序的python程序可能会挂起很长时间,所以我想检查超时并终止它。我认为 System() 可能不会因超时而终止?
      • @user534498:啊,我明白了。更新了答案,但遗憾的是你想做的在 PHP 中是不可行的(至少没有自定义 PHP 扩展)。
      【解决方案3】:

      父进程很难跟踪所有它的子进程。仅会通知进程直接儿童死亡。您可以采用几种不同的机制来尝试做您想做的事情,但最好是重新编写您的应用程序以不关心您的进程的孙子进程。

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

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

      • 在您的进程中打开一个pipe(2),并在一个足够高的文件描述符中将创建的pipe(7) 的读取端提供给子进程,以使其不会被“意外”使用。 (dup2(fd[0], 20) 可能不错。)确保您的其他程序不会关闭意外的文件描述符。 使用 fcntl(2) 的写入端 (fd[1]) 设置 O_NONBLOCK 标志F_SETFL 命令。

        定期尝试将一个字节写入管道。最终,您将收到EPIPE 错误返回,表明读取端已被所有子项关闭,或者您将收到错误返回,其中errno 设置为EAGAIN,表示管道已满 em> 还有一个孩子在跑。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-21
        • 1970-01-01
        • 1970-01-01
        • 2013-11-08
        • 2021-11-07
        • 2020-09-09
        相关资源
        最近更新 更多