不幸的是,&& 的作用或多或少与您想要的相反。 :-) 当您在 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 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