运行 通过 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"');

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

而不是压力测试

编辑::

这是我现在拥有的:

我需要 运行 强调 "test" ,但 cpu 限制在 sudo apache 或 root 下,因为它需要特殊权限。压力仍然开始正常,但它吃掉了 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

不幸的是,&& 或多或少与您想要的相反。 :-) 当您在 Bash 中执行 A && B 时,这意味着 "Run command A and wait until it's done; if it succeeded, then run command B."

相比之下,A & B表示,"Run command A and then immediately run command B."

所以你的命令几乎是正确的,但只是被两个 bash 命令(应该只需要一个)和 &&.

绊倒了

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

查看 stress 的文档,我发现它是通过分叉子进程来工作的,所以你试图 CPU-limit 的是父进程,但不是使用的那个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

然后,在另一个终端 运行ning 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 剩余 "listening" 个具有该名称的进程。返回 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 Whosebug-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检查。)

运行 top 在 60 秒内在另一个终端中 PHP 脚本是 运行ning,我看到:

  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 运行ning 在用户 test 下,cpulimit 运行ning 在用户 运行ning 下用户 root(如果需要,您可以在命令中更改两者)。 stress 限制在 30% 左右。

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

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