Introduction
The goal of these exercises is to explore the functionalities of
Linux cgroups
. This mechanism allows to assign resource management
and monitoring controllers to processes and, this way, limit their
actions or monitor their activity.
This work requires a Linux host, which can be virtualised.
Resources used in the guide: cgroups.zip
Mounted cgroups
cgroup
controllers are nowadays set up at boot time. The set of
running cgroup
containers is listed by file /proc/cgroups
.
In each line, you have a readable cgroup
name, a file system
hierarchy index, the number of cgroups
in that
hierarchy and its enabled status.
cat /proc/cgroups
Note: to increase the readability of the output you may change the terminal’s tabbing schema with the command:
tabs 16
before listing the cgroups
. To restore the normal tab spacing use the command
reset
cgroups
form a hierarchy, which is observable and managed as a file
system hierarchy. This hierarchy is created by mounting cgroup
controllers on the file system mount point /sys/fs/cgroup
.
The list of mounted cgroups
can be observed with:
mount -t cgroup
for version 1 cgroups
or
mount -t cgroup2
for version 2 cgroups
.
For version 1, each line represents a cgroup
hierarchy, and some hierarchies have
several controllers
-
this is the case of:
-
cgroups
net_cls
andnet_prio
-
cgroups
cpu
andcpuacct
.
For version 2 there should be a single, unified hierarchy.
Each directory below each hierarchy represents a cgroup
within that
hierarchy. The processes that belong to that group have their PID
listed in the cgroup
file cgroup.procs
. In each hierarchy, a
process cannot belong to more than one cgroup
.
cgroups
of a process
The cgroups
that a process belongs to are listed by the file
cgroup
in the process /proc
directory. For your
current shell, whose PID
is given by $$
, its cgroups
can
be listed as follows:
cat /proc/$$/cgroup
With cgroups
version 1, you get many lines, since different
controllers have different paths under /sys/fs/cgroup
.
With cgroups
version 2, which uses a unified tree, you get a
single line, because all the controllers can be managed from a
single directory (there is no splitting at a higher hierarchical
level, as we have with version 1).
By default, new processes belong to the same cgroups
of
their ancestors. This way, any limitations imposed to a process by a
cgroup
will naturally extend to its process descendants, thus
encompassing all those processes in the same limitative scope.
Creation and application of new cgroups
New cgroups
can be created in the intended hierarchy, and below a
given cgroup
, with a simple mkdir
command. However, you need
privileges to do so.
Assume you have an application that you want to run with a given set
of limits imposed by cgroups
. We can use the a program to do it: a (limited)
fork bomb (fork-bomb.c
).
This program creates a high amount of processes (though limited to $100$), which can be
further limited with a pids
cgroup
.
Create a console (which runs a shell command interpreter) and check its cgroup
with the command
cat /proc/$$/cgroup
The lines presented by the previous command start with a number
(zero for cgroups
version 2), followed by :
and a cgroup
name (empty for cgroups
version 2), followed again by :
and by
the cgroup
path.
The output should present one or more paths to the shell’s cgroups
, which
should be reachable under /sys/fs/cgroup
. We are interested
only in controlling the number of processes in a cgroup
, so for
version 1 use only the path corresponding to the pids
cgroup
(should start with the path /sys/fs/cgroup/pids
).
For version 2 use the unique path that is presented.
Since this cgroup
path can change from system to system, we will
simply refer to it as the shell’s cgroup
path and we will use a
shell variable (SCGP
), to refer it. You can set this
variable with this command:
SCGP=...
where the ellipsis stands for the shell’s cgroup
path.
Change the shell’s current directory to its cgroup
path:
cd $SCGP
and create a directory p_limit
on it:
mkdir p_limit
and change the shell’s current directory to it:
cd p_limit
If using cgroups
version 2, check the contents of the file
cgroup.type
:
cat cgroup.type
This file indicates the type of the cgroup
, which by default
should be domain invalid
. With this type, the cgroup
does not accept threads (or processes) registered directly on it. To
allow that, change its type to threaded
with the following
command:
echo threaded > cgroup.type
Check again the cgroup
type, it should now be threaded
.
If using cgroups
version 2, check the contents of the file
cgroup.controllers
:
cat cgroup.controllers
This file lists all the controllers that are currently active in
this cgroup
. If it does not present the pids
controller
(the one that enables limiting the number of processes), you must
add it with the following command:
echo +pids > cgroup.controllers
Check again the cgroup
controllers, they should now include the
pids
.
Upon this command, you should be able to observe that a set of files
starting by the stem pids.
appeared:
ls -la
We will use this cgroup
to limit the number of processes that can be
created by the fork bomb. Let’s say, 10. Thus, see first what is the
limited imposed by the new group:
cat pids.max
You will be presented with the value max
, which means the
maximum value permitted by the cgroup
above in the hierarchy. To
change this value to 10
, run this command:
echo 10 > pids.max
Run the cat
command again to verify the new limit of 10
processes in the cgroup
.
Now, consider the program cgroup.c
, that launches
an arbitrary command within a given set of cgroups
(this program
is prepared to work with cgroups
of versions 1 and 2):
Change the shell’s current directory to one where you can store and compile the fork bomb and this other program.
After their compilation, use cgroup
to run fork-bomb
in the newly created cgroup
:
./cgroup $SCGP/p_limit -c fork-bomb
Verify that the fork bomb cannot create more than 9
processes.
Since each process created by the fork bomb lasts for 10
seconds,
you can observe the presence of their PID in the cgroup
cgroup.procs
file:
cat $SCGP/p_limit/cgroup.procs
Repeat this command until you see that all processes have left the
cgroup
(upon their termination).
Now, if you repeat the controlled launching of the fork bomb again several times, fast, you will see that you will probably succeed only the first time
- in the next ones the fork bomb will not work at all. Explain why.
Upon launching the fork bomb with the p_limit
cgroup
, you
can observe its use by one of the processes in that group (you need
to be fast, or to increase the lifetime of the processes created by
the fork bomb):
cat /proc/`head -1 $SCGP/p_limit/cgroup.procs`/cgroup
Now run the fork bomb without the limiting cgroup
:
./fork-bomb
In this case, it will be able to create 100
new processes.
Add your actual shell to the cgroup
we have been using:
echo $$ > $SCGP/p_limit/cgroup.procs
Execute again the fork bomb, without any control, and see what happens.
Answer this question: how can the shell continue to execute commands while the processes of the fork bomb are still running? All commands? Try a pipeline (a sequence of commands connected by a pipe). Did it work? Explain.
Once useless, you can remove the cgroup
by acting on the cgroups
file system:
sudo rmdir $SCGP/p_limit
Note: you may simply remove the directory (the cgroup
) without
having to remove all the files (cgroup
attributes) that the
cgroup
contains.
Homework
Experiment to use the memory
controller to create a cgroup
that limits the amount of memory a process can use. The limit can be
tested with the consecutive allocation of 4KiB
chunks.