systemd 资源控制:cgroup v2 配置与实践

七月 18, 2026 [Linux] #systemd #cgroup #linux #资源控制

Linux cgroup(control group)是内核提供的一种机制,用于对进程组进行资源限制、监控和隔离。systemd 将其作为服务管理的基础设施,通过单元配置文件即可完成 CPU、内存、IO 等资源的分配与控制。

本文基于 cgroup v2 内核文档systemd.resource-control(5) 手册,结合实际操作示例,整理 systemd 资源控制的完整用法。

cgroup v1 vs v2

cgroup v1 自 Linux 2.6.24 引入,存在多个问题:每个控制器可挂载到独立层级,导致管理复杂;不同控制器间行为不一致;缓冲写入(buffered write)在 IO 控制器中未被正确统计。

cgroup v2 从 Linux 4.5 开始稳定,采用统一层级(unified hierarchy),所有控制器挂载在单一树形结构下,解决了 v1 的诸多问题。

对比项cgroup v1cgroup v2
层级结构每个控制器独立层级单一统一层级
CPU 权重cpu.shares(范围 2-1024)cpu.weight(范围 1-10000,默认 100)
CPU 带宽cpu.cfs_quota_uscpu.max
内存硬限制memory.limit_in_bytesmemory.max
内存保护 / 节流不支持memory.lowmemory.high(仅 v2)
内存交换限制无独立控制memory.swap.max
IO 控制器blkioio
IO 权重blkio.weightio.weight
缓冲写入统计不统计正确统计
设备控制独立 devices 子系统eBPF 程序
进程迁移通过 tasks 文件支持线程级独立迁移仅进程级,线程模式有严格限制

同一种资源只能用 v1 或 v2 其中一种方式控制。 如果 systemd 配置中同时设置了 v1 和 v2 的选项,v2 优先。

systemd 资源控制配置选项

以下选项在单元的 [Service][Slice][Scope][Socket][Mount][Swap] 段落中设置。

CPU 控制

CPUWeight= / StartupCPUWeight= — CPU 时间权重分配(仅 cgroup v2)

取值范围 1–10000,默认 100。权重值决定了同一 slice 内各单元之间的 CPU 时间分配比例,值越大获得 CPU 时间越多。StartupCPUWeight= 仅在系统启动和关机阶段生效。

[Service]
CPUWeight=200
StartupCPUWeight=500

取代旧版本的 CPUShares= / StartupCPUShares=

CPUQuota= — CPU 时间配额上限

百分比值,表示最大可占用单核 CPU 的百分比。可超过 100% 以使用多核:

CPUQuota=50%    # 最多使用半个核
CPUQuota=200%   # 最多使用两个核

对应 cgroup v2 的 cpu.max 或 cgroup v1 的 cpu.cfs_quota_us

CPUAffinity= — CPU 亲和性

将进程绑定到指定 CPU 核心运行,可在服务文件中配置:

[Service]
CPUAffinity=3

等价于在命令行使用 taskset 工具:

taskset -p -c 2 8928                # 绑定到 CPU 2
taskset -p -c 3,5,7-10 8928         # 绑定到多个 CPU

内存控制

MemoryAccounting= — 开启内存统计

[Service]
MemoryAccounting=true

MemoryLow= — 内存保护下限(仅 cgroup v2)

当单元内存使用低于此值时将被保护,内存回收时优先回收未受保护的单元。单位支持 KMGT(以 1024 为基数),也支持百分比值。

MemoryLow=512M

MemoryHigh= — 内存节流上限(仅 cgroup v2)

超过此值后进程被降速并积极回收内存,不会触发 OOM killer。infinity 表示不限。

MemoryHigh=2G

MemoryMax= — 内存硬限制

超过此限制将触发 OOM killer 杀死进程。取代旧版本的 MemoryLimit=

MemoryMax=4G

MemorySwapMax= — Swap 使用硬限制

MemorySwapMax=1G

注意MemoryLimit= 是 cgroup v1 的旧参数,在 v2 下应使用 MemoryMax=。旧参数在 systemd 中仍被支持(自动映射),但建议全部采用新语法。

IO 控制

IOAccounting= — 开启 Block IO 统计

IOAccounting=true

IOWeight= / StartupIOWeight= — IO 权重(仅 cgroup v2)

取值范围 1–10000,默认 100。取代旧版本的 BlockIOWeight=

IOWeight=500

IODeviceWeight= — 设备级 IO 权重

IODeviceWeight=/dev/sda 1000

IOReadBandwidthMax= / IOWriteBandwidthMax= — 磁盘读写带宽上限(仅 cgroup v2)

IOReadBandwidthMax=/dev/sda 50M
IOWriteBandwidthMax=/dev/sda 30M

取代旧版本的 BlockIOReadBandwidth= / BlockIOWriteBandwidth=

IOReadIOPSMax= / IOWriteIOPSMax= — 磁盘读写 IOPS 上限

IOReadIOPSMax=/dev/sda 1K

进程数控制

TasksAccounting= — 开启 task 统计

TasksAccounting=true

TasksMax= — 最大 task 数限制,对应 cgroup v2 的 pids.max

TasksMax=512

设备访问控制

DeviceAllow= — 控制对设备节点的读写和创建权限

格式为 设备名 权限,权限为 r(读)、w(写)、m(mknod 创建设备节点)的组合。

DeviceAllow=/dev/sda5 rw
DeviceAllow=char-pts rw

DevicePolicy= — 设备访问策略

网络控制

IPAccounting= — 开启 IPv4/IPv6 网络流量统计

IPAccounting=true

IPAddressAllow= / IPAddressDeny= — 基于 eBPF 的 IP 地址过滤

IPAddressDeny=any
IPAddressAllow=192.168.1.0/24

支持特殊名字:anylocalhostlink-localmulticast。需要内核开启 eBPF 支持。

Slice 与 Delegate

Slice= — 指定单元所属的 slice

Slice=test2.slice

Delegate= — 将 cgroup 子层级的管理权交给进程自身

Delegate=yes

开启后,单元可以在自己的 cgroup 下创建和管理子层级,systemd 不再干预其 cgroup 结构。适用于容器运行时、用户实例管理器等需要自主管理 cgroup 的场景。

实战:用户级别资源限制

临时生效(运行时修改)

使用 systemctl set-property 在运行时修改资源限制,立即生效但重启后丢失:

systemctl set-property user-1000.slice CPUQuota=300%
systemctl set-property user-1000.slice MemoryMax=2G

查看生效的配置:

cat /etc/systemd/system.control/user-1000.slice.d/*

永久生效(创建 slice 单元文件)

创建 /usr/lib/systemd/system/user-1000.slice

[Unit]
Description=User 1000 resource limit slice

[Slice]
MemoryAccounting=true
MemoryMax=2000M
CPUQuota=300%

启用并启动:

systemctl daemon-reload
systemctl start user-1000.slice
systemctl -t slice                     # 查看所有 slice
systemctl status user-1000.slice -l    # 查看状态

每个用户默认有一个 user-<UID>.slice,可直接用 set-property 修改或放置 drop-in 配置:

# /etc/systemd/system/user-1000.slice.d/50-memory.conf
[Slice]
MemoryMax=4G

systemd-run 启动测试进程

systemd-run --unit=toptest2 --slice=test2 top -b

查看状态:

systemctl status toptest2.service

可以看到进程位于指定 slice 的 cgroup 下:

● toptest2.service - /usr/bin/top -b
   Main PID: 8928 (top)
   CGroup: /test2.slice/toptest2.service
           └─8928 /usr/bin/top -b

限制内存

创建一个消耗内存的脚本进行测试:

systemd-run --unit=memorytest1 --slice=test2 /usr/bin/bash /tmp/highmem.sh
systemctl set-property memorytest1.service MemoryMax=2G

通过 top 观察进程内存使用不会超过限制(例如 128G 服务器设定 2G,使用率约 1.5%)。

限制磁盘读取速度

systemd-run --unit=ioread --slice=test2 dd if=/dev/sdk of=/dev/null
# iotop 显示读取速度 ~180M/s

systemctl set-property ioread IOReadBandwidthMax='/dev/sdk 50M'
# iotop 显示读取速度降至 ~50M/s

参考链接: