cgroups
控制组(通常称为 cgroups)是 Linux 内核提供的一项功能,用于管理、限制和审计进程组。与其他方法(如 nice(1) 命令或 /etc/security/limits.conf
)相比,cgroups 更加灵活,因为它们可以对进程的(子)集(可能具有不同的系统用户)进行操作。
可以使用各种工具访问控制组
- 在 systemd 单元文件中使用指令来指定服务和切片的限制;
- 直接访问
cgroup
文件系统; - 通过诸如
cgcreate
、cgexec
和cgclassify
之类的工具(libcgroupAUR 和 libcgroup-gitAUR 软件包的一部分); - 使用“规则引擎守护程序”来自动将某些用户/组/命令移动到组(
/etc/cgrules.conf
和cgconfig.service
)(libcgroupAUR 和 libcgroup-gitAUR 软件包的一部分);以及 - 通过其他软件,例如 Linux 容器 (LXC) 虚拟化。
对于 Arch Linux,systemd 是调用和配置 cgroups 的首选和最简单方法,因为它是默认安装的一部分。
安装
确保您已安装以下软件包之一以进行自动化 cgroup 处理
- systemd - 用于控制 systemd 服务的资源。
- libcgroupAUR, libcgroup-gitAUR - 一组独立工具(
cgcreate
、cgclassify
,通过cgconfig.conf
持久化)。
使用 systemd
层级结构
可以使用 systemctl status
或 systemd-cgls
命令查看当前的 cgroup 层级结构。
$ systemctl status
● myarchlinux State: running Jobs: 0 queued Failed: 0 units Since: Wed 2019-12-04 22:16:28 UTC; 1 day 4h ago CGroup: / ├─user.slice │ └─user-1000.slice │ ├─user@1000.service │ │ ├─gnome-shell-wayland.service │ │ │ ├─ 1129 /usr/bin/gnome-shell │ │ ├─gnome-terminal-server.service │ │ │ ├─33519 /usr/lib/gnome-terminal-server │ │ │ ├─37298 fish │ │ │ └─39239 systemctl status │ │ ├─init.scope │ │ │ ├─1066 /usr/lib/systemd/systemd --user │ │ │ └─1067 (sd-pam) │ └─session-2.scope │ ├─1053 gdm-session-worker [pam/gdm-password] │ ├─1078 /usr/bin/gnome-keyring-daemon --daemonize --login │ ├─1082 /usr/lib/gdm-wayland-session /usr/bin/gnome-session │ ├─1086 /usr/lib/gnome-session-binary │ └─3514 /usr/bin/ssh-agent -D -a /run/user/1000/keyring/.ssh ├─init.scope │ └─1 /sbin/init └─system.slice ├─systemd-udevd.service │ └─285 /usr/lib/systemd/systemd-udevd ├─systemd-journald.service │ └─272 /usr/lib/systemd/systemd-journald ├─NetworkManager.service │ └─656 /usr/bin/NetworkManager --no-daemon ├─gdm.service │ └─668 /usr/bin/gdm └─systemd-logind.service └─654 /usr/lib/systemd/systemd-logind
查找进程的 cgroup
进程的 cgroup 名称可以在 /proc/PID/cgroup
中找到。
例如,shell 的 cgroup
$ cat /proc/self/cgroup
0::/user.slice/user-1000.slice/session-3.scope
cgroup 资源使用情况
可以使用 systemd-cgtop
命令查看资源使用情况
$ systemd-cgtop
Control Group Tasks %CPU Memory Input/s Output/s user.slice 540 152,8 3.3G - - user.slice/user-1000.slice 540 152,8 3.3G - - user.slice/u…000.slice/session-1.scope 425 149,5 3.1G - - system.slice 37 - 215.6M - -
自定义 cgroups
systemd.slice(5) systemd 单元文件可用于定义自定义 cgroup 配置。它们必须放置在 systemd 目录中,例如 /etc/systemd/system/
。可以在 systemd.resource-control(5) 中找到可以分配的资源控制选项的文档。
这是一个示例切片单元,仅允许使用一个 CPU 的 30%
/etc/systemd/system/my.slice
[Slice] CPUQuota=30%
请记住执行 daemon-reload 以加载任何新的或更改的 .slice
文件。
作为服务
服务单元文件
资源可以直接在服务定义中或作为drop-in 文件指定
[Service] MemoryMax=1G
此示例将服务限制为 1 GB。
在切片下分组单元
可以指定服务在哪个切片中运行
[Service] Slice=my.slice
作为 root 用户
systemd-run
可用于在特定切片中运行命令。
# systemd-run --slice=my.slice command
--uid=username
选项可用于以特定用户身份生成命令。
# systemd-run --uid=username --slice=my.slice command
--shell
选项可用于在切片内生成命令 shell。
作为非特权用户
如果满足某些条件,非特权用户可以将提供给他们的资源划分为新的 cgroups。
必须使用 Cgroups v2 以允许非 root 用户管理 cgroup 资源。
控制器类型
并非所有资源都可以由用户控制。
控制器 | 可以由用户控制 | 选项 |
---|---|---|
cpu | 需要委托 | CPUAccounting, CPUWeight, CPUQuota, AllowedCPUs, AllowedMemoryNodes |
io | 需要委托 | IOWeight, IOReadBandwidthMax, IOWriteBandwidthMax, IODeviceLatencyTargetSec |
memory | 是 | MemoryLow, MemoryHigh, MemoryMax, MemorySwapMax |
pids | 是 | TasksMax |
rdma | 否 | ? |
eBPF | 否 | IPAddressDeny, DeviceAllow, DevicePolicy |
用户委托
为了让用户控制 cpu 和 io 资源,需要委托资源。这可以使用drop-in 文件完成。
例如,如果您的用户 ID 是 1000
/etc/systemd/system/user@1000.service.d/delegate.conf
[Service] Delegate=cpu cpuset io
重启并验证您的用户会话所在的切片是否具有 cpu 和 io 控制器
$ cat /sys/fs/cgroup/user.slice/user-1000.slice/cgroup.controllers
cpuset cpu io memory pids
用户自定义切片
用户切片文件可以放置在 ~/.config/systemd/user/
中。
要在特定切片下运行命令
$ systemd-run --user --slice=my.slice command
您也可以在切片内运行您的登录 shell
$ systemd-run --user --slice=my.slice --shell
运行时调整
可以使用 systemctl set-property
命令在运行时调整 cgroups 资源。选项语法与 systemd.resource-control(5) 中的语法相同。
--runtime
选项,否则调整将是永久性的。调整保存在系统范围选项的 /etc/systemd/system.control/
和用户选项的 .config/systemd/user.control/
中。例如,切断所有用户会话的互联网访问
$ systemctl set-property user.slice IPAddressDeny=any
使用 libcgroup
您可以启用带有 systemd 的 cgconfig
服务。这使您可以更轻松地跟踪 cgconfig.conf
中的任何错误。
临时组
cgroups 的强大功能之一是您可以动态创建“临时”组。您甚至可以授予常规用户创建自定义组的权限。groupname
是 cgroup 名称
# cgcreate -a user -t user -g memory,cpu:groupname
现在您的用户可以写入组 groupname
中的所有可调参数
$ ls -l /sys/fs/cgroup/memory/groupname
total 0 -rwxrwxr-x 1 user root 0 Sep 25 00:39 cgroup.event_control -rwxrwxr-x 1 user root 0 Sep 25 00:39 cgroup.procs -rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.rt_period_us -rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.rt_runtime_us -rwxrwxr-x 1 user root 0 Sep 25 00:39 cpu.shares -rwxrwxr-x 1 user root 0 Sep 25 00:39 notify_on_release -rwxrwxr-x 1 user root 0 Sep 25 00:39 tasks
Cgroups 是分层的,因此您可以根据需要创建任意数量的子组。如果普通用户想要在名为 foo
的新子组下运行 bash
shell
$ cgcreate -g memory,cpu:groupname/foo $ cgexec -g memory,cpu:groupname/foo bash
为了确保(仅对旧版 (v1) cgroups 有意义)
$ cat /proc/self/cgroup
11:memory:/groupname/foo 6:cpu:/groupname/foo
为此组创建了一个新的子目录。要将此组中所有进程的内存使用量限制为 10 MB,请运行以下命令
$ echo 10000000 > /sys/fs/cgroup/memory/groupname/foo/memory.limit_in_bytes
请注意,内存限制仅适用于 RAM 使用量 - 一旦任务达到此限制,它们将开始交换。但这不会显着影响其他进程的性能。
同样,您可以更改此组的 CPU 优先级(“份额”)。默认情况下,所有组都有 1024 个份额。具有 100 个份额的组将获得约 10% 的 CPU 时间
$ echo 100 > /sys/fs/cgroup/cpu/groupname/foo/cpu.shares
您可以通过列出 cgroup 目录来找到更多可调参数或统计信息。
您还可以更改已运行进程的 cgroup。要将所有 'bash' 命令移动到此组
$ pidof bash 13244 13266 $ cgclassify -g memory,cpu:groupname/foo `pidof bash` $ cat /proc/13244/cgroup 11:memory:/groupname/foo 6:cpu:/groupname/foo
持久组配置
如果您希望在启动时创建 cgroups,则可以在 /etc/cgconfig.conf
中定义它们。例如,“groupname”具有 $USER
和组 $GROUP
的用户的管理限制和添加任务的权限。子组 “groupname/foo”组定义如下所示
/etc/cgconfig.conf
group groupname { perm { # who can manage limits admin { uid = $USER; gid = $GROUP; } # who can add tasks to this group task { uid = $USER; gid = $GROUP; } } # create this group in cpu and memory controllers cpu { } memory { } } group groupname/foo { cpu { cpu.shares = 100; } memory { memory.limit_in_bytes = 10000000; } }
- 注释应从行首开始!注释的 # 字符必须作为行的第一个字符出现。否则,cgconfigparser 将在解析时遇到问题,但只会报告
cgroup change of group failed
错误,除非您使用 Systemd 启动了 cgconfig - 权限部分是可选的。
- 包含所有控制器子目录的
/sys/fs/cgroup/
层级目录已在启动时创建并挂载为虚拟文件系统。这使得可以使用$CONTROLLER-NAME { }
命令创建新的组条目。如果出于任何原因,您想在另一个位置创建和挂载层级结构,那么您将需要在/etc/cgconfig.conf
中编写第二个条目,方法如下
mount { cpuset = /your/path/groupname; }
这等效于以下 shell 命令
# mkdir /your/path/groupname # mount -t /your/path -o cpuset groupname /your/path/groupname
使用 cgroup 虚拟文件系统
从 systemd 232 开始,下一节中描述的 cgm 方法,本节将改为描述手动限制内存使用的方法。
创建一个名为 groupname 的新 cgroup
# mkdir /sys/fs/cgroup/memory/groupname
示例:将最大内存限制设置为 100MB
# echo 100000000 > /sys/fs/cgroup/memory/groupname/memory.limit_in_bytes
将进程移动到 cgroup
# echo pid > /sys/fs/cgroup/memory/groupname/cgroup.procs
示例
限制命令的内存或 CPU 使用
以下示例显示了一个 cgroup,它将给定命令限制为 2GB 内存。
$ systemd-run --scope -p MemoryMax=2G --user command
以下示例显示了一个命令,该命令被限制为一个 CPU 核心的 20%。
$ systemd-run --scope -p CPUQuota="20%" --user command
Matlab
在 MATLAB 中进行大型计算可能会使您的系统崩溃,因为 Matlab 没有任何保护措施来防止占用您机器的所有内存或 CPU。以下示例显示了一个 cgroup,它将 Matlab 限制为前 6 个 CPU 核心和 5 GB 内存。
使用 systemd
~/.config/systemd/user/matlab.slice
[Slice] AllowedCPUs=0-5 MemoryHigh=6G
像这样启动 Matlab(确保使用正确的路径)
$ systemd-run --user --slice=matlab.slice /opt/MATLAB/2012b/bin/matlab -desktop
使用 libcgroup
/etc/cgconfig.conf
group matlab { perm { admin { uid = username; } task { uid = username; } } cpuset { cpuset.mems="0"; cpuset.cpus="0-5"; } memory { memory.limit_in_bytes = 5000000000; } }
将 username
更改为运行 Matlab 的用户。
您还可以使用 cpu
约束来限制 CPU 份额。
像这样启动 Matlab(确保使用正确的路径)
$ cgexec -g memory,cpuset:matlab /opt/MATLAB/2012b/bin/matlab -desktop
文档
- 要了解关于控制器以及特定开关和可调参数的更多信息,请参考内核文档 v1 或 v2 (或者安装 linux-docs 并查看
/usr/src/linux/Documentation/cgroup
) - Linux 手册页:cgroups(7)
- 详细且完整的资源管理指南可以在 Red Hat Enterprise Linux 文档中找到。
对于命令和配置文件,请参阅相关的手册页,例如 cgcreate(1) 或 cgrules.conf(5)
技巧与提示
启用 cgroup v1
Cgroup v2 现在默认启用,cgroups v1 被认为是过时的,并且自 systemd v256 起系统将拒绝启动。[1] 如果您想切换到 cgroup v1,您需要设置以下内核参数
SYSTEMD_CGROUP_ENABLE_LEGACY_FORCE=1 systemd.unified_cgroup_hierarchy=0