1

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

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

4

3 回答 3

2

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

注意:等待另一个进程可能会导致您的脚本由于超过max_execution_time. 它也可能无法很好地与您的 Web 服务器的执行单元的内部管理配合使用。

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

于 2011-12-08T01:18:59.273 回答
2

proc_get_status 可以获取子进程的pid

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

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

pcntl_waitpid($pid, $child_status);

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

请参阅PCNTL 函数

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

首先:SIGALARM在你的主进程中安装一个处理程序

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

pcntl_signal(SIGALARM, term_proc);

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

pcntl_alarm($seconds)

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

于 2011-12-08T01:29:54.800 回答
2

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

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

    While you can use cgroups yourself, I haven't learned much about the consequences of trying to use cgroups for process management while systemd or libvirt or other tools might be doing similar things. (That's my ignorance more than a limitation of cgroups -- perhaps it handles it beautifully. I like to dream.)

  • Open a pipe(2) in your process and give the read end of the pipe(7) created to the child process in a file descriptor high enough that it won't be used "accidentally". (dup2(fd[0], 20) is probably good.) Make sure your other programs don't close unexpected file descriptors. Set the O_NONBLOCK flag on the writing end (fd[1]) using fcntl(2)'s F_SETFL command.

    Periodically try to write a byte into the pipe. Eventually, you'll either get EPIPE error returns indicating that the read end has been closed by all children OR you'll get an error return with errno set to EAGAIN meaning the pipe is full and there is still a child running.

于 2011-12-08T01:40:50.547 回答