CGroup Practical Experiments
A cgroup (control group) is a Linux kernel feature that organises processes into hierarchical groups, enabling the management (limiting, accounting and isolating) of system resources such as CPU, memory, disk I/O, and network bandwidth.
Each cgroup is associated with a set of limits or parameters, which are enforced by the kernel through subsystems (sometimes also known as resource controllers, or simply controllers).
All cgroup functionalities are accessed through the cgroup filesystem
(/sys/fs/cgroup
). This is a virtual filesystem with special files that
act as the interface for creating, removing, or altering cgroups.
To see available cgroup subsystems on your Linux OS:
❯ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 50 1
cpu 0 50 1
cpuacct 0 50 1
blkio 0 50 1
memory 0 50 1
devices 0 50 1
freezer 0 50 1
net_cls 0 50 1
perf_event 0 50 1
net_prio 0 50 1
hugetlb 0 50 1
pids 0 50 1
rdma 0 50 1
misc 0 50 1
Experiments
Prerequisites
Before starting experiments, install following tools:
apt install -y cgroup-tools iperf3
Experiment 1: Memory Limitation
Let's create a simple program that continuously allocates memory:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
// Allocated times
size_t times = 0;
// 1 MiB
size_t blockSize = 1024 * 1024;
while (1) {
int *ptr = (int *) malloc(blockSize);
// This is necessary or the actual memory won't be allocated.
for (int j = 0; j < blockSize/sizeof(int); ++j) {
ptr[j] = j;
}
++times;
printf("\rMemory allocated: %ldMiB", times);
fflush(stdout);
usleep(2000);
}
return 0;
}
Compile this program and run it under a memory-limited cgroup:
# Create the memory cgroup
cgcreate -g memory:/my_mem
# The unit is actually 500MiB
echo 500M > /sys/fs/cgroup/my_mem/memory.max
# Run the program within the cgroup
cgexec -g memory:/my_mem ./a.out
The final output:
Memory allocated: 496 MiBzsh: killed cgexec -g memory:/my_mem ./a.out
Experiment 2: Network Throttling with net_cls
Kernel module net_cls (Network Classifier CGroup) may not be enabled by default. To enable it:
mkdir /sys/fs/cgroup/net_cls
mount -t cgroup -onet_cls net_cls /sys/fs/cgroup/net_cls
Egress Limiting
Steps:
- Use net_cls to tag network packages
- Use
tc
to control the traffic
Limitations for the following method:
- Does not work for WiFi interface (the reason has not been investigated)
- If you want to control both ingress and egress, consider using virtual interfaces further
- Only one QDISC can be added to a network interface
First create a new net_cls cgroup:
cgcreate -g net_cls:limited_bw
Assign a class ID to the cgroup (class ID format is 0xAAAABBBB
,
where AAAA
is the major number and BBBB
is the minor number):
# Set a a 10:1 handle
cgset -r net_cls.classid=0x00100001 limited_bw
# Alternatively
echo 0x00100001 > /sys/fs/cgroup/net_cls/limited_bw/net_cls.classid
# Check the value, 0x00100001 equals 1048577
cat /sys/fs/cgroup/net_cls/limited_bw/net_cls.classid
1048577
Then control traffic using tc
:
-
Add a queueing discipline HTB (Hierarchy Token Bucket) to shape outbound traffic:
tc qdisc add dev enp1s0 root handle 10: htb
qdisc
: queueing discipline10:
is the qdisc-id in format<major>:
(also called handle), this id should be consistent with the cgroup class ID major number
-
Create a traffic class with limit:
tc class add dev enp1s0 parent 10: classid 10:1 htb rate 1mbps
10:1
is the class-id in format<major>:<minor>
, the major number must be consistent with parent handle, the minor number must be consistent with minor number of the cgroup1mbps
means 1 mega-bytes per second
-
Add a filter to associate traffic from the cgroup with this class
tc filter add dev enp1s0 parent 10: handle 1: cgroup
1:
is the filter-id, don't need to be consistent with the cgroup class ID minor number (tested)
Starting a new process to test:
cgexec -g net_cls:limited_bw iperf3 -s
# Need to test on another machine
iperf3 -c <server_ip> -R
Alternatively, apply to an existing process:
cgclassify -g net_cls:limited_bw ${pid}
For convenience, here is a helper script.