cgroups

出自 ArchWiki

此条目或章节有待与 systemd 合并。

注意: 主要关于不再适用于 systemd 系统的信息;cgroup v1 已过时 (在 Talk:cgroups#Archive and merge systemd-run examples into systemd page 中讨论)

控制组 (通常称为 cgroups) 是 Linux 内核提供的一项功能,用于管理、限制和审计进程组。与其他方法(如 nice(1) 命令或 /etc/security/limits.conf)相比,cgroups 更加灵活,因为它们可以对进程的(子)集(可能具有不同的系统用户)进行操作。

可以使用各种工具访问控制组

  • systemd 单元文件中使用指令来指定服务和切片的限制;
  • 直接访问 cgroup 文件系统;
  • 通过诸如 cgcreatecgexeccgclassify 之类的工具(libcgroupAURlibcgroup-gitAUR 软件包的一部分);
  • 使用“规则引擎守护程序”将某些用户/组/命令自动移动到组(/etc/cgrules.confcgconfig.service)(libcgroupAURlibcgroup-gitAUR 软件包的一部分);以及
  • 通过其他软件,例如 Linux 容器 (LXC) 虚拟化。

对于 Arch Linux,systemd 是调用和配置 cgroup 的首选和最简单方法,因为它是默认安装的一部分。

安装

确保您已安装以下软件包之一以进行自动 cgroup 处理

  • systemd - 用于控制 systemd 服务的资源。
  • libcgroupAUR, libcgroup-gitAUR - 独立工具集(cgcreatecgclassify,通过 cgconfig.conf 持久化)。

使用 systemd

层级结构

可以使用 systemctl statussystemd-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        -        -

自定义 cgroup

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。

作为非特权用户

如果满足某些条件,非特权用户可以将提供给他们的资源划分为新的 cgroup。

必须使用 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
注意: eBPF 从技术上讲不是控制器,但那些使用它实现的 systemd 选项只有 root 用户才能设置。

用户委托

为了让用户控制 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 命令在运行时调整 cgroup 资源。选项语法与 systemd.resource-control(5) 中的相同。

警告: 除非传递 --runtime 选项,否则调整将是永久性的。调整保存在系统范围选项的 /etc/systemd/system.control/ 和用户选项的 .config/systemd/user.control/ 中。
注意: 并非所有资源更改都会立即生效。例如,更改 TaskMax 只会在生成新进程时生效。

例如,切断所有用户会话的互联网访问

$ systemctl set-property user.slice IPAddressDeny=any

使用 libcgroup

您可以启用带有 systemd 的 cgconfig 服务。这使您可以更轻松地跟踪 cgconfig.conf 中的任何错误。

临时组

cgroup 的强大功能之一是您可以动态创建“临时”组。您甚至可以授予普通用户创建自定义组的权限。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

Cgroup 是分层的,因此您可以根据需要创建任意数量的子组。如果普通用户想要在名为 foo 的新子组下运行 bash shell

$ cgcreate -g memory,cpu:groupname/foo
$ cgexec    -g memory,cpu:groupname/foo bash

为了确保(仅对旧版 (v1) cgroup 有意义)

$ 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

持久组配置

注意: 当使用 Systemd ≥ 205 管理 cgroup 时,您可以完全忽略此文件。

如果您希望在启动时创建 cgroup,则可以在 /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 虚拟文件系统

此条目或章节需要扩充。

原因: 本节必须与以下各节集成(在 Talk:Cgroups 中讨论)

从 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
注意: 在最后一条命令中,一次只能写入一个 PID,因此对于每个必须移动的进程重复此操作。

示例

限制命令的内存或 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

文档

  • 有关控制器以及某些开关和可调参数的含义的信息,请参阅内核文档 v1v2(或安装 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

参见