---
title: cgroup
url: https://doc.liz6.com/ja/linux-kernel/12-containers-and-primitives/02-cgroup
locale: ja
area: linux-kernel
tags:
- linux-kernel
- containers-and-primitives
date: 2026-06-30
modified: 2026-07-16
description: 'カバー範囲: cgroup v2 の統一階層 → コントローラ (cpu/memory/io/pids/cpuset) → cgroupfs → メモリリクライム → PSI → cgroup プログラミングモデル カーネルバージョン: 2.6 (v1) ~ 6.x (v2)'
---

# 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 インターフェース

```bash
# マウント
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

```bash
# ウェイト (スケジューリング優先度, デフォルト 100):
echo 200 > /sys/fs/cgroup/mygroup/cpu.weight   # CPU の 2 倍

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

### memory

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

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

### io

```bash
# ウェイト:
echo "8:0 200" > io.weight  # デバイス 8:0 のウェイト 200

# バンド幅制限:
echo "8:0 rbps=1048576 wbps=2097152" > io.max
```

### pids

```bash
echo 100 > pids.max        # この cgroup で最大 100 個のプロセス
echo pids.max              # 超えると fork() が失敗 (EAGAIN)
```

### cpuset

```bash
# CPU/メモリノードの固定:
echo "0-3" > cpuset.cpus          # CPU 0-3 のみ使用可能
echo "0" > cpuset.mems            # NUMA ノード 0 のメモリのみ使用可能
```

---

## メモリリクライムの詳細

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

<svg viewBox="0 0 720 400" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="try_charge 内存计费决策流程:依次检查 memory.max 与 memory.high,触发拒绝、reclaim 或放行">
  <defs>
    <marker id="cgArrow" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker>
  </defs>
  <rect width="720" height="400" fill="#ffffff"/>
  <text x="360" y="26" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">try_charge() カーネルパス: 割り当て時の2段階チェック</text>

  <rect x="230" y="44" width="260" height="32" rx="7" fill="#4f46e5"/>
  <text x="360" y="64" text-anchor="middle" font-size="12.5" font-weight="700" fill="#ffffff">try_charge(memcg, gfp, nr_pages)</text>
  <line x1="360" y1="76" x2="360" y2="90" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>

  <rect x="230" y="92" width="260" height="38" rx="7" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="116" text-anchor="middle" font-size="12" font-weight="600" fill="#3730a3">usage + nr_pages &gt; memory.max ?</text>
  <line x1="360" y1="130" x2="360" y2="144" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>
  <text x="376" y="141" font-size="10.5" fill="#64748b">No</text>

  <rect x="230" y="146" width="260" height="38" rx="7" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="360" y="170" text-anchor="middle" font-size="12" font-weight="600" fill="#3730a3">usage + nr_pages &gt; memory.high ?</text>
  <line x1="360" y1="184" x2="360" y2="198" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>
  <text x="376" y="195" font-size="10.5" fill="#64748b">No</text>

  <rect x="230" y="200" width="260" height="34" rx="7" fill="#dcfce7" stroke="#4ade80"/>
  <text x="360" y="222" text-anchor="middle" font-size="12" font-weight="700" fill="#166534">OK → memcg-&gt;memory-&gt;usage += nr_pages</text>

  <line x1="490" y1="111" x2="556" y2="111" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>
  <text x="500" y="105" font-size="10.5" fill="#64748b">Yes</text>
  <rect x="560" y="92" width="130" height="38" rx="7" fill="#ffffff" stroke="#ef4444" stroke-width="1.4"/>
  <text x="625" y="110" text-anchor="middle" font-size="11.5" font-weight="700" fill="#dc2626">拒否</text>
  <text x="625" y="124" text-anchor="middle" font-size="10" fill="#dc2626">ENOMEM / OOM</text>

  <line x1="490" y1="165" x2="556" y2="165" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>
  <text x="500" y="159" font-size="10.5" fill="#64748b">Yes</text>
  <rect x="560" y="146" width="130" height="38" rx="7" fill="#ffedd5"/>
  <text x="625" y="169" text-anchor="middle" font-size="11" font-weight="600" fill="#9a3412">memcg リクライムをトリガー</text>

  <line x1="625" y1="184" x2="620" y2="196" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>
  <rect x="520" y="198" width="200" height="32" rx="7" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="620" y="218" text-anchor="middle" font-size="11" fill="#115e59">try_to_free_mem_cgroup_pages()</text>

  <line x1="620" y1="230" x2="620" y2="242" stroke="#475569" stroke-width="1.6" marker-end="url(#cgArrow)"/>
  <rect x="500" y="244" width="240" height="32" rx="7" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="620" y="264" text-anchor="middle" font-size="11" fill="#0f766e">shrink_lruvec() → LRU スキャン → リクライム</text>

  <rect x="40" y="298" width="640" height="82" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="56" y="320" font-size="12.5" fill="#115e59">リクライムの優先度 (usage + nr_pages &gt; memory.high でトリガー時):</text>
  <text x="56" y="342" font-size="12.5" fill="#115e59">① この cgroup の不要なキャッシュ (page cache / slab) をリクライム → ② 不十分なら、この cgroup の匿名ページをスワップ</text>
  <text x="56" y="364" font-size="12.5" fill="#115e59">→ ③ それでも不十分で memcg-&gt;oom_group の場合 → 整個 cgroup を kill</text>
</svg>

---

## PSI (Pressure Stall Information)

```bash
# /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 以外の全タスクが待機中 → リソースの完全な飽和
```

---

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

```c
// 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*
