On this page

cgroup

Coverage: cgroup v2 unified hierarchy → controllers (cpu/memory/io/pids/cpuset) → cgroupfs → memory reclaim → PSI → cgroup programming model Kernel versions: 2.6 (v1) ~ 6.x (v2)

Overview

cgroup (control group) provides resource usage limits, priority control, and statistics for processes. v2 unifies the multiple independent hierarchies of v1 into a single tree, solving the fundamental problem of lack of coordination between controllers.

cgroup v2 vs v1

cgroup v1:
  Each controller has its own hierarchy → CPU and memory control trees may differ
  → Cannot express "the CPU and memory limits in this cgroup act together"

cgroup v2 (4.5+, current default):
  Single unified hierarchy → all controllers share the same tree
  → Set parameters for all controllers in the same cgroup directory
  → Coordination between controllers becomes possible (e.g., memory pressure → IO throttling)

cgroupfs Interface

# Mount
mount -t cgroup2 none /sys/fs/cgroup

# Create cgroup
mkdir /sys/fs/cgroup/mygroup
# → Automatically inherits parent controller settings

# Move process
echo $PID > /sys/fs/cgroup/mygroup/cgroup.procs

# View process's cgroup
cat /proc/$PID/cgroup

Controller Details

cpu

# Weight (scheduling priority, default 100):
echo 200 > /sys/fs/cgroup/mygroup/cpu.weight   # 2x CPU

# Bandwidth limit:
echo "50000 100000" > cpu.max  # 50% of one CPU (50ms per 100ms)

memory

echo 1G > memory.max       # Hard limit (exceeding → OOM kill)
echo 800M > memory.high     # Soft limit (exceeding → throttle + reclaim)
echo 100M > memory.low       # Best-effort guarantee (try to retain when memory is tight)

# Memory pressure notification (PSI):
cat memory.pressure  # 10s/60s/300s averages for some/full

io

# Weight:
echo "8:0 200" > io.weight  # Device 8:0 weight 200

# Bandwidth limit:
echo "8:0 rbps=1048576 wbps=2097152" > io.max

pids

echo 100 > pids.max        # Max 100 processes in this cgroup
echo pids.max              # Exceeding → fork() fails (EAGAIN)

cpuset

# Pin CPU/memory nodes:
echo "0-3" > cpuset.cpus          # Can only use CPUs 0-3
echo "0" > cpuset.mems            # Can only use memory from NUMA node 0

Memory Reclaim Deep Dive

mm/memcontrol.c — Kernel path for cgroup memory controller:

try_charge() Kernel Path: Two-Level Check During Allocation try_charge(memcg, gfp, nr_pages) usage + nr_pages > memory.max ? No usage + nr_pages > memory.high ? No OK → memcg->memory->usage += nr_pages Yes Reject ENOMEM / OOM Yes Trigger memcg reclaim try_to_free_mem_cgroup_pages() shrink_lruvec() → Scan LRU → Reclaim Reclaim priority (triggered when usage + nr_pages > memory.high): ① Reclaim useless cache (page cache / slab) for this cgroup → ② If insufficient, swap anonymous pages of this cgroup → ③ If still insufficient and memcg->oom_group → Kill entire cgroup

PSI (Pressure Stall Information)

# /proc/pressure/ — Three levels of pressure metrics:
cat /proc/pressure/cpu     # some/total percentage
cat /proc/pressure/memory  # some/full
cat /proc/pressure/io      # some/full

# some: At least one task is waiting → Resource contention
# full: All non-idle tasks are waiting → Resources fully saturated

Programming Interface (Kernel Side)

// include/linux/cgroup.h
// Implementation of controllers in the kernel:

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 (one per cgroup)
// blkio: css attached to request_queue

References

  • Source Code: kernel/cgroup/cgroup.c (core framework), mm/memcontrol.c (memory), block/blk-cgroup.c (io), kernel/sched/core.c (cpu)
  • Kernel Documentation: Documentation/admin-guide/cgroup-v2.rst (Excellent)
  • LWN: "cgroup v2", "Memory control group design"

Keywords: cgroup v2, controllers, cpu.weight, memory.max, io.max, PSI, memcg reclaim, cgroupfs