【问题标题】:PHP hanging while exec() bash scriptPHP在exec()bash脚本时挂起
【发布时间】:2015-03-01 01:59:54
【问题描述】:

我有一些看起来像这样的代码行...

exec($this->path.' start > /dev/null 2>&1 &');
return ['status' => 'Command executed'];

$this->path 是一个 shell 脚本,start 是 shell 脚本的一个参数,我相信该行的其余部分应该转储任何响应,以便 php 脚本可以继续运行。它没有像它应该的那样工作,php 成功启动了 shell 脚本(它启动了一个游戏服务器)但是 php 只是挂起,直到我使用 shell 关闭服务器。当我使用 shell 关闭服务器时,它完成执行并且我收到“命令执行”响应。我还禁用了 SELinux 的强制执行以确保它不会干扰。

运行 Linux - Fedora 21 和内置的 PHP 开发服务器。

【问题讨论】:

    标签: php linux bash shell


    【解决方案1】:

    我在生产环境中解决了同样的问题,如下所示:

    pclose(popen($this->path.' start > /dev/null 2>&1 &', 'r'));
    

    所以,诀窍是启动服务器,然后关闭进程文件指针

    希望有帮助:)

    【讨论】:

    • 你有同样的行为吗?
    • 是的,完全一样。
    • 我重试了,结果和以前一样,服务器启动但是php挂起,直到服务器停止。
    【解决方案2】:

    另一种(丑陋的)方法是在屏幕会话中实际运行您的 shell 脚本。

    exec('screen -dmS -X ' . $this->path . ' start');
    

    【讨论】:

      【解决方案3】:

      我相信该行的其余部分是假设转储任何响应,以便 php 脚本可以继续运行

      如果你不明白,这里是解释。如果你有:

      exec($this->path.' start > /dev/null 2>&1 &');
      

      > /dev/null 部分表示将 stdout(即命令产生的常规输出)重定向到 /dev/null(即空设备)。因此,命令本身产生的任何输出都将被抑制。

      2>&1 部分表示将 stderror(即执行命令产生的任何错误)重定向到 stdout。然而,由于标准输出被重定向到 /dev/null,任何错误也将被重定向到那里。因此,使用这两个,它会抑制该命令将产生的任何消息。

      最后,最后的&(和号)将命令分叉到一个新进程。来自Bash man page:

      如果一个命令被控制操作符 & 终止,shell 在子shell 的后台执行命令。外壳可以 不等待命令完成,返回状态为 0(真)。

      但是,根据this question,您正在做的事情应该有效。必须有其他事情发生以阻止进程成功分叉。只是为了排除 PHP 的问题,我首先尝试通过命令行而不是通过 PHP 的 exec 执行命令。如果还是不行,我猜是因为你的Job Control有问题。要么它以某种方式被禁用。我没有在 PHP 中尝试过,但您可以使用 set -m 命令(启用作业控制)启用它。请注意,要禁用作业控制而不是 set -m,请执行 set +m。以下是您可以在 PHP 中执行此操作的方法:

      exec('set -m && ' . $this->path.' start > /dev/null 2>&1 &');
      

      您可以做的另一件事是在 PHP 脚本执行时,登录到命令行并输入命令 jobs 并查看其输出。如果它是空白的,PHP 没有正确分叉作业。您应该会看到如下内容:

      [1]+  Stopped                 your_command.sh
      

      注意这里是如何写的stopped。如果进程仍在运行,这可能不应该是 stopped。

      您可以做的另一件事是查看checkjobs 是启用还是禁用。登录服务器,执行以下获取内置的shell optional behavior:

      shopt -p | grep checkjobs
      

      如果输出是shopt -u checkjobs,这不是问题。如果它改为显示shopt -s checkjobs,这可能会导致您看到的行为,因为杀死带有后台作业的 shell 将导致错误说有作业正在运行并且您实际上必须杀死 shell 两次摆脱它。也许这是 PHP 开发人员没有考虑到的。在这种情况下,请在 PHP 中的命令之前添加 shopt -u checkjobs &&。

      exec('shopt -u checkjobs && ' . $this->path.' start > /dev/null 2>&1 &');
      

      【讨论】:

      • 我将set -m && 部分添加到我的代码中。当通过 shell jobs 执行时,返回一行“完成”。这是正确的操作,因为脚本只是创建自己的进程然后退出。当我通过 php 执行该行时,我得到与往常一样的结果,jobs 根本不返回任何内容。只是为了清楚shell脚本确实总是成功执行,但是当通过php完成时它会挂起php。
      • checkjobs 行也返回了shopt -u checkjobs
      • 尝试删除> /dev/null 2>&1 &部分并通过PHP执行它以查看是否有任何输出。可能是权限问题或其他原因导致它被 PHP 而不是命令行挂起。
      • 我的代码在运行 nginx 和 php-fpm 时可以正常工作。不知道为什么它不适用于 php 开发服务器。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-28
      • 2014-04-07
      • 1970-01-01
      • 2023-04-11
      • 2015-02-23
      • 2015-04-07
      • 2012-12-15
      相关资源
      最近更新 更多