このページの目次

cgroup

カバー範囲: cgroup v2 の統一階層 → コントローラ (cpu/memory/io/pids/cpuset) → cgroupfs → メモリリクライム → PSI → cgroup プログラミングモデル カーネルバージョン: 2.6 (v1) ~ 6.x (v2)

概要

cgroup (control group) は、プロセスのリソース使用制限、優先度制御、および統計情報を提供します。v2 は v1 の複数の独立した階層を 1 つの木構造に統合し、コントローラ間の調整が不可能だった根本的な問題を解決しました。

cgroup v2 vs v1

cgroup v1:
  各コントローラに独立した階層がある → CPU と memory の制御ツリーが異なる可能性がある
  → 「この cgroup 内で CPU と memory の上限が一緒に機能する」という表現ができない

cgroup v2 (4.5 以降、現在のデフォルト):
  単一の統一階層 → すべてのコントローラが同じ木構造を共有
  → 1 つの cgroup ディレクトリ内ですべてのコントローラのパラメータを設定可能
  → コントローラ間の調整が可能になる (例: memory pressure → IO スロージング)

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   # CPU の 2 倍

# バンド幅制限:
echo "50000 100000" > cpu.max  # 1 CPU の 50% (100ms につき 50ms)

memory

echo 1G > memory.max       # ハード上限 (超えると OOM kill)
echo 800M > memory.high     # ソフト上限 (超えるとスロットル + リクライム)
echo 100M > memory.low       # ベストエフォート保証 (メモリ逼迫時にできるだけ保持)

# メモリプレッシャー通知 (PSI):
cat memory.pressure  # some/full の 10秒/60秒/300秒平均

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 ノード 0 のメモリのみ使用可能

メモリリクライムの詳細

mm/memcontrol.c — cgroup メモリコントローラのカーネルパス:

try_charge() カーネルパス: 割り当て時の2段階チェック 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 拒否 ENOMEM / OOM Yes memcg リクライムをトリガー try_to_free_mem_cgroup_pages() shrink_lruvec() → LRU スキャン → リクライム リクライムの優先度 (usage + nr_pages > memory.high でトリガー時): ① この cgroup の不要なキャッシュ (page cache / slab) をリクライム → ② 不十分なら、この cgroup の匿名ページをスワップ → ③ それでも不十分で memcg->oom_group の場合 → 整個 cgroup を kill

PSI (Pressure Stall Information)

# /proc/pressure/ — 3段階の圧力指標:
cat /proc/pressure/cpu     # some/total のパーセンテージ
cat /proc/pressure/memory  # some/full
cat /proc/pressure/io      # some/full

# some: 少なくとも1つのタスクが待機中 → リソース競合
# full: idle 以外の全タスクが待機中 → リソースの完全な飽和

プログラミングインターフェース (カーネル側)

// include/linux/cgroup.h
// カーネル内のコントローラの実装:

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 ごとに1つ)
// 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