Security, Reverse Engineering, Cloud and Code

Using Linux CGroups to Cap CPU Usage

Revx0r
Revx0r
July 2, 2021

Using Linux CGroups to Cap CPU Usage

There may come a time in your time where you may want to limit the amount of CPU a process/program uses on a machine for a number of different reasons. I found the best way to do it is using CGroups. You can dive and get into the weeds of CGroups here: man cgroups. As a side note/bonus, containers use CGroups to help provides the container(s) (e.g. Docker/containerd) that isolation.

For my testing purposes, I wanted to limit the CPU usage to 5%. We can do this by using the cpu.cfs_quota_us and cpu.cfgs_period_us properties of the CGroup. To provide the correct ratio you just do a bit of basic math to bring you to the target usage percentage. What it comes down to here is the following: cpu.cfs_quota_us / cpu.cfgs_period_us = CPU Usage Percent Cap.

Thus for my purposes I will be using the following values

cpu.cfs_quota_us =   10000
cpu.cfgs_period_us = 200000

To verify, 10000 / 200000 = 0.05 == 5%, my target CPU usage cap.

Setting up the CGroup

Pre-requisite Install the bins to help us work with CGroups (Ubuntu/Deb):

sudo apt install cgroup-tools

For Red Hat, Fedora use dnf/yum; Arch use Pacman, etc…

First we create our new CGroup

sudo cgcreate -g cpu:cpulimited

Let’s set the cpu.cfs_quota_us and cpu.cfgs_period_us properties

sudo cgset -r cpu.cfs_quota_us=10000 cpulimited
sudo cgset -r cpu.cfgs_period_us=200000 cpulimited

Altenatively, you could also use this method if you run into issues:

echo 10000 > /sys/fs/cgroup/cpu/cpulimited/cpu.cfs_quota_us
echo 200000 > /sys/fs/cgroup/cpu/cpulimited/cpu.cfs_period_us

NOTE: Adjust your path accordingly and check it is correct!

Let’s check the properties that we have given our CGroup to inspect what you expect!

sudo cgget -g cpu:cpulimited

Now we can run our program/script like so:

sudo cgexec -g cpu:cpulimited ./your_program_or_script.sh

Important Note: I ran into an issue where cgexec would not behave right. It would not cap the usage as I expected. The "command" I was running had a lot of pipes, so it was really multiple commands. There is a --sticky flag that you can used and it suppoed to have all child processed fall under the same cgroup but that did not work in my case. After a lot of troubleshooting, to solve this I put everything in a shell script. Therefore, to save your self some trouble, just throw your command in a shell script and run that instead 🙂

If you are going through all this trouble, you might also be interested in the execution time. You can get that by using the time utility like so:

time sudo cgexec -g cpu:cpulimited ./your_program_or_script.sh

Restricting already running processes

To restrict already running processes you can use cgclassify to reassign the proc(s) to your new cgroup as follows:

cgclassify -g cpu:cpulimited <pidNum>

Reference:

  • https://linuxhint.com/limit_cpu_usage_process_linux/
  • https://www.programmersought.com/article/30481340400/
  • https://github.com/libcgroup/libcgroup/blob/main/README
Revx0r
  • I am an Offensive Security Engineer doing security shenanigans and playing in the cloud

Uncategorized