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


Running two linux commands via PHP, don't wait for first to end before executing second

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

我正在尝试对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"');

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

编辑::

这是我现在拥有的:

我需要在"测试"下运行压力,但是 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 https://i.stack.imgur.com/730Eu.png

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

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

因此,您的命令接近正确,但只是通过使用两个bash命令(应该只需要一个)和&&而被绊倒。

另外,您是否尝试在 PHP 之外的两个终端中分别运行每个命令?我刚刚下载并构建了压力和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

好多了。(请注意,在 Bash shell 外部,您用 exec -a 将其别名化,您将看到进程名称为 stress 。不幸的是,我还看到了另一个问题,即cpulimit"侦听"使用该名称的更多进程。返回 cpulimit --help ,其中显示了-z选项。

只是为了稍微降低复杂性,您可以关闭别名并通过特殊的 Bash 变量 $! 使用 stress 进程的 PID,它指的是最后一个启动的进程的 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 stackoverflow-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/stackoverflow/stress 
my-user ALL=(root) NOPASSWD:NOEXEC: /home/my-user/development/stackoverflow/cpulimit