Unfortunately, the &&
does more or less the opposite of what you want. :-) When you do A && B
in Bash, that means, "Run command A and wait until it's done; if it succeeded, then run command B."
By contrast, A & B
means, "Run command A and then immediately run command B."
So you're close to right in your command, but just getting tripped up by using two bash
commands (should only need one) and the &&
.
Also, did you try running each command separately, outside PHP, in two terminals? I just downloaded and built both stress and cpulimit (I assume these are the ones you're using?), ran the commands separately, and spotted a problem: cpulimit
still isn't limiting the percentage.
Looking at the docs for stress
, I see it works by forking child processes, so the one you're trying to CPU-limit is the parent, but that's not the one using the CPU. cpulimit --help
reveals there's option -i
, which includes child processes in what is limited.
This gets me to the point of being able to enter this in one terminal (first line shows input at the prompt; subsequent show output):
$> 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
Then, in another terminal running top
, I see:
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
Much better. (Notice that outside the Bash shell where you aliased it with exec -a
, you will see the process name as stress
.) Unfortunately, I also see another issue, which is cpulimit
remaining "listening" for more processes with that name. Back to cpulimit --help
, which reveals the -z
option.
Just to reduce the complexity a bit, you could leave the alias off and use the PID of the stress
process, via the special Bash variable $!
, which refers to the PID of the last process launched. Running the following in a terminal seems to do everything you want:
stress -c 1 -t 60s & cpulimit -p $! -l 30 -i -z
So now, just change the PHP script with what we've learned:
exec('bash -c "exec -a MyUniqueProcessName stress -c 1 -t 60s & cpulimit -e MyUniqueProcessName -l 30 -i -z"');
...or, simpler version:
exec('bash -c "stress -c 1 -t 60s & cpulimit -p \$! -l 30 -i -z"');
(Notice the $ in the $!
had to be escaped with a backslash, \$!
, because of the way it's quoted when passed to bash -c
.)
Final Answer:
Based on the last example you amended to your question, you'll want something like this:
passthru('bash -c "sudo -u test stress -c 1 -t 60s & sudo -u root cpulimit -p \$! -l 30 -i -z"');
When I run this with php stackoverflow-question.php
, it outputs the following:
stress: info: [3374] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: info: [3374] successful run completed in 60s
Process 3371 found
(The second two lines only appear after the PHP script finishes, so don't be mislead. Use top
to check.)
Running top
in another terminal during the 60 seconds the PHP script is running, I see:
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
This is exactly what you've described wanting: stress
is running under the user test
, and cpulimit
is running under the user root
(both of which you can change in the command, if desired). stress
is limited to around 30%.
I'm not familiar with runuser
and don't see the applicability, since sudo
is the standard way to run a process as another user. To get this to work, you may have to adjust your settings in /etc/sudoers (which will require root access, but you obviously already have that). That's entirely outside the scope of this discussion, but as an example, I added the following rules:
my-user ALL=(test) NOPASSWD:NOEXEC: /home/my-user/development/stackoverflow/stress
my-user ALL=(root) NOPASSWD:NOEXEC: /home/my-user/development/stackoverflow/cpulimit