このページの目次
Slab と kmalloc
カバー範囲: SLUB アロケータ → kmem_cache → kmalloc/kfree → スラブマージ → デバッグ (KASAN/SLUB_DEBUG) → SLOB の廃止 (6.4) カーネルバージョン: 2.6 ~ 6.x
概要
Buddy アロケータの最小割り当て単位は 1ページ(4KB)です。しかし、カーネル内のほとんどの割り当ては 4KB よりもはるかに小さく、task_struct(約 4KB)、inode(約 600B)、dentry(約 200B)などです。もし小さなオブジェクトごとに 1ページ全体を占有させると、その無駄は極めて深刻なものになります。
SLUB アロケータはこの問題を解決します:1ページ(または連続する数ページ)を同じサイズの小さなオブジェクトに分割し、freelist(空きリスト)を使って空きオブジェクトと割り当て済みオブジェクトを管理します。SLUB は現在のデフォルトのスラブ実装であり(6.4 で古い SLOB が削除されました)、SLAB (Solaris → Linux 2.2) から派生し、SLUB (Linux 2.6.23, C. Lameter) として実装されました。
SLUB アーキテクチャ
kmem_cache
// include/linux/slub_def.h
;
Per-CPU ホットスラブ
flowchart TD
START["SLUB 割り当て/解放リクエスト"]
START --> OP{"操作タイプ?"}
OP -->|"割り当て"| ALLOC["freelist ヘッドからオブジェクトを取得<br/>(アトミック: this_cpu_cmpxchg)"]
ALLOC --> FULL{"現在のスラブ<br/>がいっぱい?"}
FULL -->|"いいえ"| FAST_ALLOC["✅ オブジェクトを返す<br/>(ロックレス、極めて高速)"]
FULL -->|"はい"| PART{"部分的な<br/>スラブがある?"}
PART -->|"はい"| SWITCH["次の部分的スラブに切り替え"]
SWITCH --> FAST_ALLOC
PART -->|"いいえ"| BUDDY_ALLOC["buddy から新しいスラブを取得"]
BUDDY_ALLOC --> FAST_ALLOC
OP -->|"解放"| FREE["オブジェクトを freelist ヘッドに戻す"]
FREE --> EMPTY{"スラブが完全に空になった?"}
EMPTY -->|"はい"| RETURN["buddy に返還される可能性あり"]
EMPTY -->|"いいえ"| FAST_FREE["✅ 解放完了<br/>(ロックレス、極めて高速)"]
classDef fast fill:#e8f5e9,stroke:#2e7d32
classDef slow fill:#ffebee,stroke:#c62828
classDef decision fill:#fff3e0,stroke:#ef6c00
class START,ALLOC,FREE fast
class FULL,PART,EMPTY decision
class SWITCH,BUDDY_ALLOC,RETURN slow
class FAST_ALLOC,FAST_FREE fast
割り当てと解放パス
// mm/slub.c
// 割り当て (高速パス → 低速パス):
→ →
├─ fast path: cpu_slab の freelist から取得
│ object = ;
│ if
│ // freelist ポインタを更新 (cmpxchg)
│ return object;
│
└─ slow path:
├─ CPU partial リスト内のスラブを試す
├─ ノード partial リスト内のスラブを試す
├─ どちらも失敗 → →
│ → → buddy アロケータ
│ → freelist を構築
└─ 最初のオブジェクトを返す
// 解放 (高速パス):
→
├─ fast path: オブジェクトを cpu_slab の freelist に戻す
│ ;
│
└─ slow path:
├─ スラブが完全に空き状態になったか? → per-node partial が少ない場合 → partial に変換
│ → そうでない場合 → buddy アロケータに解放
└─ ノード統計を更新
kmalloc: 汎用割り当てインターフェース
// include/linux/slab.h
// kmalloc は実際には事前に定義された kmem_cache のラッパー
// カーネル内で事前に定義された汎用キャッシュ:
// kmalloc-8, kmalloc-16, kmalloc-32, kmalloc-64,
// kmalloc-96, kmalloc-128, kmalloc-192, kmalloc-256,
// kmalloc-512, kmalloc-1k, kmalloc-2k, kmalloc-4k, kmalloc-8k
// kmalloc(size, flags):
// 1. size に応じて対応する kmem_cache を検索 (kmalloc_caches テーブル経由)
// 2. kmem_cache_alloc(s, flags) を呼び出す
// 3. オブジェクトポインタを返す
// kfree(ptr):
// 1. オブジェクトのメタデータから、それがどの kmem_cache に属しているかを見つける
// → page->slab_cache (struct page 内のフィールド) を介して
// 2. kmem_cache_free(cache, ptr) を呼び出す
スラブマージ
// mm/slab_common.c
// メモリを節約するために、サイズが同じまたは近い kmem_cache はマージできる:
// "dentry" (256B) と "inode_cache" (592B)
// → どちらも kmalloc-512 から割り当てられる? → マージしない: サイズが異なる
// → しかし、2つのキャッシュの object_size が近い場合 (どちらも 256B に近い)
// → 基盤となる同じスラブを共有する可能性がある
//
// /sys/kernel/slab/ 以下で、どのキャッシュがマージされているかを確認できる
SLOB の廃止 (6.4)
歴史的に Linux には 3 つのスラブ実装があった:
SLAB: 初期実装 (Solaris 移植 → Linux 2.2)、最も複雑
SLUB: 簡略化された実装 (2.6.23)、現在のデフォルト
SLOB: 極小実装、組み込みシステム向け (< 16MB RAM)
6.4 で SLOB が削除された:
- SLUB は長年の最適化により、コードサイズが十分に小さくなった
- 組み込みシステムでは SLUB を使用可能 (CONFIG_SLUB_TINY)
- 3つのアロケータを維持する保守負荷が大きすぎた
SLOB の設計思想は注目すべき点がある:
- per-CPU スラブがない
- すべての割り当ては単一の空きリストから行われる
- first-fit 割り当て (十分な大きさの最初のブロックを見つける)
- 極めてシンプルだが、断片化とパフォーマンスの面で劣る
kmemleak: メモリリーク検出
// mm/kmemleak.c
// ユーザー空間の valgrind の memcheck に類似
// 定期的に割り当て済みのすべてのカーネルオブジェクトをスキャンし、参照のないものを見つけてリークを報告
// 有効化:
// CONFIG_DEBUG_KMEMLEAK=y
// ブート引数: kmemleak=on
// 手動トリガー:
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak # リークレポート
KASAN: バッファオーバーラン/UAF 検出
// mm/kasan/
// Kernel Address Sanitizer — コンパイル時のインストルメンテーション
// スラブの境界外アクセス、use-after-free、ダブルフリーを検出
// 3つのモード:
// Generic KASAN: シャドウメモリ (8バイトごとに 1バイトでマーキング) → メモリオーバーヘッド 1/8
// Software Tag-Based KASAN: ARM64 で TBI (Top Byte Ignore) を使用 → 低オーバーヘッド
// Hardware Tag-Based KASAN: ARM64 MTE → ハードウェアチェック → 本番環境向け
デバッグと観測
# スラブの使用状況
# 列: name, active_objs, num_objs, objsize, objperslab, pagesperslab
# より人間 readable なバージョン
# 特定キャッシュの詳細情報
# 最も多くのスラブを使用しているものを確認 (kmem trace)
|
# メモリリーク検出
|
参考と拡張
- カーネルドキュメント:
Documentation/mm/slub.rst,Documentation/dev-tools/kmemleak.rst,Documentation/dev-tools/kasan.rst - LWN:
- "The SLUB allocator" (lwn.net/Articles/229984/)
- "Removing SLOB" (lwn.net/Articles/967178/)
- ソースファイル:
mm/slub.c— SLUB 実装mm/slab_common.c— スラブマージ、sysfs インターフェースinclude/linux/slub_def.h— struct kmem_cachemm/kmemleak.c— リーク検出mm/kasan/— KASAN
キーワード: SLUB, kmem_cache, kmalloc, per-CPU スラブ, freelist, スラブマージ, SLOB 廃止, kmemleak, KASAN