cgroup是内核级强制资源控制机制,能硬性限制CPU、内存、IO等资源;超限时进程被OOM killer杀掉或等待下个调度周期。

Linux cgroup 是什么,它真能限制进程资源吗
能,而且是内核级强制控制。cgroup(control group)不是用户态工具或监控代理,而是 Linux 内核提供的资源隔离与分组管理机制。只要正确挂载、配置并把进程加入对应 cgroup,CPU、内存、IO 等资源上限就会被硬性执行——超限时,比如内存用超 memory.max,进程会直接被 OOM killer 杀掉;CPU 时间超配额,就只能等下个调度周期。
为什么 memory.max 设了却没生效
常见原因是没启用 memory controller,或者进程没真正进入目标 cgroup。从 kernel 5.8+ 开始,memory controller 默认不启用,需在启动参数中显式添加 cgroup_enable=memory 并配合 swapaccount=1(若需限制 swap)。另外,仅创建目录、写入 memory.max 不够,必须把进程 PID 写入该 cgroup 的 cgroup.procs 或 tasks 文件:
echo $PID > /sys/fs/cgroup/mygroup/cgroup.procs
注意:cgroup.procs 写入的是线程组 ID(即主线程 PID),tasks 才是单个线程 ID;混用会导致部分线程未受控。
- systemd 启动的服务默认在自己的 scope 下,需用
SystemMaxMemory或MemoryMax=在 unit 文件里配置,而非手动改 cgroup 文件 - 容器运行时(如 runc)通常自动设置 cgroup,但 debug 时建议检查
/proc/$PID/cgroup确认进程归属 -
memory.max为max表示不限制,不是“最大值”,别误设成字符串 "max"
cpu.max 和 cpu.weight 到底怎么选
两者定位完全不同:cpu.weight(cgroup v2)是相对权重,用于公平调度,不设硬上限;cpu.max 才是绝对配额,格式为 "max us/sec",例如 "50000 100000" 表示每 100ms 最多用 50ms CPU 时间。
实际场景中:
- 做资源保障型服务(如数据库主实例),优先用
cpu.max防止单一进程吃满 CPU - 做批处理任务混部(如日志压缩 + 实时 API),用
cpu.weight更灵活,避免低权任务完全饿死 -
cpu.max对短时突发无效——它只在周期性配额检查点(默认 100ms)生效,高频小任务可能连续抢到多个周期的额度
为什么 cgroup v1 和 v2 混用会出问题
根本原因是两者不兼容:v1 按子系统挂载(cpu、memory 各自独立挂载),v2 是统一挂载单棵树,所有控制器必须同时启用或禁用。如果你看到 /sys/fs/cgroup/cpu 和 /sys/fs/cgroup/unified 同时存在,说明系统处于混合模式,此时 systemd、Docker 等组件行为不可预测。
确认方式:
mount | grep cgroup
理想输出应只含一行 cgroup2 on /sys/fs/cgroup type cgroup2。若要彻底切换到 v2:
- 内核启动参数加
systemd.unified_cgroup_hierarchy=1 - 禁用旧版控制器:确保
/proc/sys/kernel/cgroup_disable为空或不含cpu、memory等 - Docker 20.10+ 默认支持 v2,但需在
/etc/docker/daemon.json中显式设"cgroup-parent": "/docker"类路径,否则可能 fallback 到 v1
cgroup 的复杂性不在语法,而在它和调度器、OOM killer、page cache 回收等内核子系统的耦合深度——一个配置项改错,可能让延迟毛刺变高,或让内存回收卡住整个节点。动手前,先看 /sys/fs/cgroup/xxx/cgroup.events 里的 populated 和 low 事件,比盲目调参更可靠。










