cgroups

出自 ArchWiki
(重定向自 Control group

此文章或章节可能需要与 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 是调用和配置 cgroups 的首选且最简单的方法,因为它包含在默认安装中。

安装

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

  • systemd - 用于控制 systemd 服务的资源。
  • libcgroupAUR, libcgroup-gitAUR - 一组独立工具 (cgcreate, cgclassify, 通过 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 中的任何错误。

临时组

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

持久组配置

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

如果您希望在启动时创建 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 文档中找到。

有关命令和配置文件,请参阅相关的 man 页面,例如 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

参见