通过 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
的方式似乎引起了问题。我的系统上没有stress
或cpulimit
,但下载并构建了它们并解决了这些问题。请参阅修改后的答案。
非常感谢您的意见。使用的两个命令确实是压力和 cpulimit,是的,我尝试从控制台运行它们。名称似乎不能正常工作,但使用 PID 则可以。工作正常。在 24 核的服务器上使用上次启动的 pid 不会导致问题吗?例如,错误的流程受到限制?我很害怕在我的单线程安全气泡之外做这样的事情
我当前的实现和更多信息在 OP 中,请查看以上是关于通过 PHP 运行两个 linux 命令,在执行第二个之前不要等待第一个结束的主要内容,如果未能解决你的问题,请参考以下文章