I want to reserve a small amount of cpu and memory to be able in case of emergency create tty session and kill a memory hogging X application. cgroups provide this functionality. How do I put all my X applications inside cgroup automatically?
Limit cpu and memory consumption of all X applications
cgroupsx
Related Solutions
UPDATE: Note that the answer below applies to RHEL 6. In RHEL 7, most cgroups are managed by systemd, and libcgroup is deprecated.
Since posting this question I have studied the entire guide that I linked to above, as well as the majority of the cgroups.txt documentation and cpusets.txt. I now know more than I ever expected to learn about cgroups, so I'll answer my own question here.
There are multiple approaches you can take. Our company's contact at Red Hat (a Technical Architect) recommended against a blanket restriction of all processes in preference to a more declarative approach—restricting only the processes we specifically wanted restricted. The reason for this, according to his statements on the subject, is that it is possible for system calls to depend on user space code (such as LVM processes) which if restricted could slow the system down—the opposite of the intended effect. So I ended up restricting several specifically-named processes and leaving everything else alone.
Additionally, I want to mention some cgroup basic data that I was missing when I posted my question.
Cgroups do not depend on libcgroup
being installed. However, that is a set of tools for automatically handling cgroup configuration and process assignments to cgroups and can be very helpful.
I found that the libcgroup tools can also be misleading, because the libcgroup package is built on its own set of abstractions and assumptions about your use of cgroups, which are slightly different than the actual kernel level implementation of cgroups. (I can put examples but it would take some work; comment if you're interested.)
Therefore, before using libcgroup tools (such as /etc/cgconfig.conf
, /etc/cgrules.conf
, cgexec
, cgcreate
, cgclassify
, etc.) I highly recommend getting very familiar with the /cgroup
virtual filesystem itself, and manually creating cgroups, cgroup hierarchies (including hierarchies with multiple subsystems attached, which libcgroup sneakily and leakily abstracts away), reassigning processes to different cgroups by running echo $the_pid > /cgroup/some_cgroup_hierarchy/a_cgroup_within_that_hierarchy/tasks
, and other seemingly magical tasks that libcgroup
performs under the hood.
Another basic concept I was missing was that if the /cgroup
virtual filesystem is mounted on your system at all (or more accurately, if any of the cgroup subsystems aka "controllers" are mounted at all), then every process on your entire system is in a cgroup. There is no such thing as "some processes are in a cgroup and some aren't".
There is what is called the root cgroup for a given hierarchy, which owns all the system's resources for the attached subsystems. For example a cgroup hierarchy that has the cpuset and blkio subsystems attached, would have a root cgroup which would own all the cpus on the system and all the blkio on the system, and could share some of those resources with child cgroups. You can't restrict the root cgroup because it owns all your system's resources, so restricting it wouldn't even make sense.
Some other simple data I was missing about libcgroup:
If you use /etc/cgconfig.conf
, you should ensure that chkconfig --list cgconfig
shows that cgconfig
is set to run at system boot.
If you change /etc/cgconfig.conf
, you need to run service cgconfig restart
to load in the changes. (And problems with stopping the service or running cgclear
are very common when fooling around testing. For debugging I recommend, for example, lsof /cgroup/cpuset
, if cpuset
is the name of the cgroup hierarchy you are using.)
If you want to use /etc/cgrules.conf
, you need to ensure the "cgroup rules engine daemon" (cgrulesengd
) is running: service cgred start
and chkconfig cgred on
. (And you should be aware of a possible but unlikely race condition regarding this service, as described in the Red Hat Resource Management Guide in section 2.8.1 at the bottom of the page.)
If you want to fool around manually and set up your cgroups using the virtual filesystem (which I recommend for first use), you can do so and then create a cgconfig.conf
file to mirror your setup by using cgsnapshot
with its various options.
And finally, the key piece of info I was missing when I wrote the following:
However, the caveat on this seems to be...that the children of myprocessname will be reassigned to the restricted cpu0only cgroup.
I was correct, but there is an option I was unaware of.
cgexec
is the command to start a process/run a command and assign it to a cgroup.
cgclassify
is the command to assign an already running process to a cgroup.
Both of these will also prevent cgred
(cgrulesengd
) from reassigning the specified process to a different cgroup based on /etc/cgrules.conf
.
Both cgexec
and cgclassify
support the --sticky
flag, which additionally prevents cgred
from reassigning child processes based on /etc/cgrules.conf
.
So, the answer to the question as I wrote it (though not the setup I ended up implementing, because of the advice from our Red Hat Technical Architect mentioned above) is:
Make the cpu0only
and anycpu
cgroup as described in my question. (Ensure cgconfig
is set to run at boot.)
Make the * cpuset cpu0only
rule as described in my question. (And ensure cgred
is set to run at boot.)
Start any processes I want unrestricted with: cgexec -g cpuset:anycpu --sticky myprocessname
.
Those processes will be unrestricted, and all their child processes will be unrestricted as well. Everything else on the system will be restricted to CPU 0 (once you reboot, since cgred
doesn't apply cgrules to already running processes unless they change their EUID). This is not completely advisable, but that was what I initially requested and it can be done with cgroups.
I think an answer is still useful here (despite this question being more than 2 years old) and what I could suggest it checking the cgred
daemon to define control group rules matching specific usernames and controllers:
https://linuxaria.com/article/introduction-to-cgroups-the-linux-conrol-group
Best Answer
I use Debian, so the solution is based on
systemd
cgroup implementation.The first step is to inspect the cgroups hierarchy:
systemd
automatically assigns cgroups to terminal sessions. In cgroup hierarchy we need determine whichsession-*.scope
has X applications in it. Default X session scope number is always the same.To set memory limit to all programs in a scope, type
This command sets memory limit to the session 2 until reboot.
To make this rule permanent, run
in text editor type
and save. This rule will persist between reboots. Other resource limits may be set in a same file.
Edit
As derobert pointed out, X session scope number is not guarantied to be the same. More robust solution is to determine this number at runtime.
File
/usr/local/bin/resource_limit.sh
:File
/etc/systemd/system/resource_limit.service
:Copy files above to your system and issue command
After this you may set specified limit (in this case 14G) using command
and unset it (setting some higher limit value) using command
In order to run this script on reboot automatically, issue command
Note 1
If your X session does not start fast enough, you may want to increase service delay via
ExecStartPre
or enable service manually.Note 2
It is a good idea to also add restrictions to
user@1000.service
(where 1000 - is a UID) with slightly bigger limit. This way you will always have resources available for system daemons.