Limit cpu and memory consumption of all X applications

cgroupsx

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?

Best Answer

I use Debian, so the solution is based on systemd cgroup implementation.

The first step is to inspect the cgroups hierarchy:

> systemd-cgls
-.slice
├─user.slice
│ └─user-1000.slice
│   ├─user@1000.service
.....
│   └─session-2.scope
│     ├─1376 lightdm --session-child 14 21
│     ├─1400 x-window-manager
.....

systemd automatically assigns cgroups to terminal sessions. In cgroup hierarchy we need determine which session-*.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

> systemctl set-property session-2.scope MemoryLimit=14G

This command sets memory limit to the session 2 until reboot.

To make this rule permanent, run

> sudo systemctl edit session-2.scope

in text editor type

[Scope]
MemoryLimit=14G

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:

#!/bin/bash

for s in $(systemd-cgls --no-pager --user-unit \
           | grep --extended-regexp --only-matching \
              'session-.{1,3}\.scope');do
    systemctl set-property --runtime "$s" MemoryLimit="$1"
done

File /etc/systemd/system/resource_limit.service:

[Unit]
Description=Limit resources
Requires=multi-user.target
After=multi-user.target

[Service]
Type=oneshot
ExecStartPre=/bin/sleep 5
ExecStart=/usr/local/bin/resource_limit.sh 14G
ExecStop=/usr/local/bin/resource_limit.sh 20G
RemainAfterExit=true

[Install]
WantedBy=graphical.target

Copy files above to your system and issue command

systemctl daemon-reload

After this you may set specified limit (in this case 14G) using command

systemctl start resource_limit.service

and unset it (setting some higher limit value) using command

systemctl stop resource_limit.service

In order to run this script on reboot automatically, issue command

systemctl enable resource_limit.service

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.

Related Question