This is pretty weird, and I searched and tried everything, but I think I'm just making a dumb syntax error here.
I'm trying to run a stress test on the CPU , then immediately limit it's cpu usage to 30% , all this via PHP. The test is also run under another user and with a specified name so it can be limited. The stress test starts fine, but I can see the PHP file still loading, and it ends the second the stress test ends.
Here's some of the ways I tried doing it
$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"');
The whole purpose of this is because I am writing a script for a game hosting website, and I want to limit the resource consumption of each server to improve quality and not let someone hog all the resources. Basically, instead of the stress test, a game server will run.
edit::
here's what I have now:
I need to run the stress under "test" , but the cpulimit under either sudo apache or root, because it requires special permissions. The stress still starts fine but it eats 99.9%
passthru('sudo runuser -l test -c "exec -a MyUniqueProcessName stress -c 1 -t 60s &" & sudo cpulimit -e MyUniqueProcessName -l 30 -i -z');
I can't see the cpulimit in the process list after doing this http://i.imgur.com/iK2nL43.png
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