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 v1 | cgroup v2 |
|---|---|---|
| 层级结构 | 每个控制器独立层级 | 单一统一层级 |
| CPU 权重 | cpu.shares(范围 2-1024) | cpu.weight(范围 1-10000,默认 100) |
| CPU 带宽 | cpu.cfs_quota_us | cpu.max |
| 内存硬限制 | memory.limit_in_bytes | memory.max |
| 内存保护 / 节流 | 不支持 | memory.low、memory.high(仅 v2) |
| 内存交换限制 | 无独立控制 | memory.swap.max |
| IO 控制器 | blkio | io |
| IO 权重 | blkio.weight | io.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)
当单元内存使用低于此值时将被保护,内存回收时优先回收未受保护的单元。单位支持 K、M、G、T(以 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= — 设备访问策略
strict:仅允许明确指定的设备访问closed:额外允许标准伪设备(/dev/null、/dev/zero、/dev/random等)auto(默认):未设置DeviceAllow=时允许所有设备
网络控制
IPAccounting= — 开启 IPv4/IPv6 网络流量统计
IPAccounting=true
IPAddressAllow= / IPAddressDeny= — 基于 eBPF 的 IP 地址过滤
IPAddressDeny=any
IPAddressAllow=192.168.1.0/24
支持特殊名字:any、localhost、link-local、multicast。需要内核开启 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
参考链接: