通过 PHP 运行两个 linux 命令,在执行第二个之前不要等待第一个结束

Posted

技术标签:

【中文标题】通过 PHP 运行两个 linux 命令,在执行第二个之前不要等待第一个结束【英文标题】:Running two linux commands via PHP, don't wait for first to end before executing second 【发布时间】:2015-05-17 08:06:07 【问题描述】:

这很奇怪,我搜索并尝试了所有方法,但我认为我只是在这里犯了一个愚蠢的语法错误。

我正在尝试对 CPU 进行压力测试,然后立即将其 cpu 使用率限制为 30%,这一切都是通过 php 完成的。该测试也在另一个用户下运行并具有指定的名称,因此可以对其进行限制。压力测试开始正常,但我可以看到 PHP 文件仍在加载,并且在压力测试结束的第二秒结束。

这是我尝试过的一些方法

$output = exec('sudo runuser -l test -c "exec -a MyUniqueProcessName stress -c 1 -t 60s & cpulimit -e MyUniqueProcessName -l 30"');

$output = exec('sudo runuser -l test -c "exec -a MyUniqueProcessName stress -c 1 -t 60s > /dev/null & cpulimit -e MyUniqueProcessName -l 30"');

这样做的全部目的是因为我正在为游戏托管网站编写脚本,并且我想限制每个服务器的资源消耗以提高质量,而不是让某人占用所有资源。 基本上,将运行游戏服务器而不是压力测试。

编辑::

这是我现在拥有的:

我需要在 "test" 下运行压力,但是在 sudo apache 或 root 下运行 cpulimit,因为它需要特殊权限。压力仍然很好,但它会吃掉 99.9%

passthru('sudo runuser -l test -c "exec -a MyUniqueProcessName stress -c 1 -t 60s &" & sudo cpulimit -e MyUniqueProcessName -l 30 -i -z');

执行此操作后在进程列表中看不到 cpulimit http://i.imgur.com/iK2nL43.png

【问题讨论】:

【参考方案1】:

不幸的是,&& 的作用或多或少与您想要的相反。 :-) 当您在 Bash 中执行 A && B 时,这意味着“运行命令 A 并等待它完成;如果成功,则运行命令 B。”

相比之下,A & B 的意思是“运行命令 A,然后立即运行命令 B。”

所以你的命令很接近正确,但只是被两个bash 命令(应该只需要一个)和&& 绊倒。

另外,您是否尝试过在 PHP 之外的两个终端中分别运行每个命令?我刚刚下载并构建了stress 和cpulimit(我假设这些是你正在使用的?),分别运行命令,发现了一个问题:cpulimit 仍然没有限制百分比。

查看stress 的文档,我发现它通过分叉子进程来工作,因此您尝试限制 CPU 的进程是父进程,但这不是使用 CPU 的进程。 cpulimit --help 揭示了有一个选项 -i,其中包括受限的子进程。

这让我能够在一个终端中输入它(第一行在提示符处显示输入;随后显示输出):

$> exec -a MyUniqueProcessName stress -c 1 -t 60s & cpulimit -e MyUniqueProcessName -l 30 -i
[1] 20229
MyUniqueProcessName: info: [20229] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
Process 20229 found

然后,在另一个运行 top 的终端中,我看到了:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM     TIME+ COMMAND
20237 stackov+  20   0  7164  100    0 R 30.2  0.0   0:04.38 stress

好多了。 (请注意,在使用 exec -a 别名的 Bash shell 之外,您将看到进程名称为 stress。)不幸的是,我还看到另一个问题,即 cpulimit 仍在“侦听”更多进程姓名。回到cpulimit --help,它显示了-z 选项。

为了稍微降低复杂性,您可以关闭别名并使用stress 进程的PID,通过特殊的Bash 变量$! 引用最后启动的进程的PID。在终端中运行以下命令似乎可以满足您的所有需求:

stress -c 1 -t 60s & cpulimit -p $! -l 30 -i -z

所以现在,只需用我们学到的内容更改 PHP 脚本:

exec('bash -c "exec -a MyUniqueProcessName stress -c 1 -t 60s & cpulimit -e MyUniqueProcessName -l 30 -i -z"');

...或者,更简单的版本:

exec('bash -c "stress -c 1 -t 60s & cpulimit -p \$! -l 30 -i -z"');

(注意$! 中的$ 必须用反斜杠\$! 转义,因为它在传递给bash -c 时的引用方式。)

最终答案:

根据您修改问题的最后一个示例,您需要这样的内容:

passthru('bash -c "sudo -u test stress -c 1 -t 60s & sudo -u root cpulimit -p \$! -l 30 -i -z"');

当我使用php ***-question.php 运行它时,它会输出以下内容:

stress: info: [3374] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: info: [3374] successful run completed in 60s
Process 3371 found

(后两行在PHP脚本完成后出现,请勿误导,请使用top查看。)

在 PHP 脚本运行的 60 秒内,在另一个终端中运行 top,我明白了:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM     TIME+ COMMAND
 3472 test      20   0  7160   92    0 R 29.5  0.0   0:07.50 stress
 3470 root       9 -11  4236  712  580 S  9.0  0.0   0:02.28 cpulimit

这正是您所描述的想要的:stress 在用户 test 下运行,cpulimit 在用户 root 下运行(如果需要,您可以在命令中更改这两者)。 stress 被限制在 30% 左右。

我不熟悉runuser 并且看不到它的适用性,因为sudo 是作为另一个用户运行进程的标准方式。要使其正常工作,您可能必须调整 /etc/sudoers 中的设置(这将需要 root 访问权限,但您显然已经拥有该权限)。这完全超出了本次讨论的范围,但作为示例,我添加了以下规则:

my-user ALL=(test) NOPASSWD:NOEXEC: /home/my-user/development/***/stress 
my-user ALL=(root) NOPASSWD:NOEXEC: /home/my-user/development/***/cpulimit 

【讨论】:

它仍然做同样的事情,运行压力测试然后冻结,如果我消除压力或让它完成然后它执行限制(php页面保持加载直到我这样做) PHP 页面保持加载,直到 bash 进程完成,即两个进程都完成。 您使用cpulimit 的方式似乎引起了问题。我的系统上没有stresscpulimit,但下载并构建了它们并解决了这些问题。请参阅修改后的答案。 非常感谢您的意见。使用的两个命令确实是压力和 cpulimit,是的,我尝试从控制台运行它们。名称似乎不能正常工作,但使用 PID 则可以。工作正常。在 24 核的服务器上使用上次启动的 pid 不会导致问题吗?例如,错误的流程受到限制?我很害怕在我的单线程安全气泡之外做这样的事情 我当前的实现和更多信息在 OP 中,请查看

以上是关于通过 PHP 运行两个 linux 命令,在执行第二个之前不要等待第一个结束的主要内容,如果未能解决你的问题,请参考以下文章

怎么在linux运行php文件?

如何通过Linux命令行使用和运行PHP脚本

求教,如何在PHP上执行iptables的命令

php在linux下配制webhook

linux下退出终端,让PHP程序继续执行的命令

怎么在linux运行php文件