本页目录

cgroup

覆盖: cgroup v2 统一层级 → 控制器 (cpu/memory/io/pids/cpuset) → cgroupfs → memory reclaim → PSI → cgroup 编程模型 内核版本: 2.6 (v1) ~ 6.x (v2)

概述

cgroup (control group) 提供进程的资源使用限制、优先级控制和统计。v2 统一了 v1 的多个独立层级为一棵树,解决了控制器之间无法协调的根本问题。

cgroup v2 vs v1

cgroup v1:
  每个控制器有独立的层级 → CPU 和 memory 的控制树可能不同
  → 无法表达"在这个 cgroup 中 CPU 和 memory 的上限是一起作用的"

cgroup v2 (4.5+, 当前默认):
  单一统一层级 → 所有控制器共享同一棵树
  → 在同一个 cgroup 目录下设置所有控制器的参数
  → 控制器之间的协调变得可能 (如 memory pressure → IO throttling)

cgroupfs 接口

# 挂载
mount -t cgroup2 none /sys/fs/cgroup

# 创建 cgroup
mkdir /sys/fs/cgroup/mygroup
# → 自动继承父级的控制器设置

# 移动进程
echo $PID > /sys/fs/cgroup/mygroup/cgroup.procs

# 查看进程所属 cgroup
cat /proc/$PID/cgroup

控制器详解

cpu

# 权重 (调度优先级, 默认 100):
echo 200 > /sys/fs/cgroup/mygroup/cpu.weight   # 2x CPU

# 带宽限制:
echo "50000 100000" > cpu.max  # 50% of one CPU (50ms per 100ms)

memory

echo 1G > memory.max       # 硬上限 (超出 → OOM kill)
echo 800M > memory.high     # 软上限 (超出 → throttle + reclaim)
echo 100M > memory.low       # 最佳努力保证 (内存紧张时尽量保留)

# 内存压力通知 (PSI):
cat memory.pressure  # some/full 的 10s/60s/300s 平均

io

# 权重:
echo "8:0 200" > io.weight  # 设备 8:0 权重 200

# 带宽限制:
echo "8:0 rbps=1048576 wbps=2097152" > io.max

pids

echo 100 > pids.max        # 此 cgroup 最多 100 个进程
echo pids.max              # 超出 → fork() 失败 (EAGAIN)

cpuset

# 钉选 CPU/内存节点:
echo "0-3" > cpuset.cpus          # 只能用 CPU 0-3
echo "0" > cpuset.mems            # 只能用 NUMA node 0 的内存

Memory Reclaim 深入

mm/memcontrol.c — cgroup memory controller 的内核路径:

try_charge() 内核路径:分配时的两级检查 try_charge(memcg, gfp, nr_pages) usage + nr_pages > memory.max ? usage + nr_pages > memory.high ? OK → memcg->memory->usage += nr_pages 拒绝 ENOMEM / OOM 触发 memcg reclaim try_to_free_mem_cgroup_pages() shrink_lruvec() → 扫描 LRU → 回收 reclaim 优先级(usage + nr_pages > memory.high 触发时): ① 回收此 cgroup 无用 cache(page cache / slab) → ② 不够则 swap 此 cgroup 的匿名页 → ③ 仍不够且 memcg->oom_group → kill 整个 cgroup

PSI (Pressure Stall Information)

# /proc/pressure/ — 三级压力指标:
cat /proc/pressure/cpu     # some/total 百分比
cat /proc/pressure/memory  # some/full
cat /proc/pressure/io      # some/full

# some: 至少一个 task 在等待 → 资源竞争
# full: 所有非 idle task 都在等待 → 资源完全饱和

编程接口 (内核侧)

// include/linux/cgroup.h
// 内核中 controller 的实现:

struct cgroup_subsys {
    struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent);
    int (*css_online)(struct cgroup_subsys_state *css);
    void (*css_free)(struct cgroup_subsys_state *css);
    // ...
};

// memcg: css = mem_cgroup (每个 cgroup 一个)
// blkio: css 挂载在 request_queue 上

参考

  • 源码⁠: kernel/cgroup/cgroup.c (核心框架), mm/memcontrol.c (memory), block/blk-cgroup.c (io), kernel/sched/core.c (cpu)
  • 内核文档⁠: Documentation/admin-guide/cgroup-v2.rst (极好)
  • LWN: "cgroup v2", "Memory control group design"

关键词: cgroup v2, controllers, cpu.weight, memory.max, io.max, PSI, memcg reclaim, cgroupfs