このページの目次
物理メモリ管理
カバー範囲: NUMA → Zone (DMA/DMA32/Normal/Movable) → Buddy Allocator → page allocator API → page struct → memory hotplug カーネルバージョン: 2.6 ~ 6.x
概要
仮想メモリ管理は「どの仮想アドレスがどの物理ページにマッピングされるか」に答えるのに対し、物理メモリ管理はより低レベルな問題を扱います。「この 4KB の物理ページは誰に属し、空闲か割り当て済みか、どの NUMA ノードから取得すべきか?」
物理メモリ管理の中核は buddy allocator(ペアアルゴリズム)です。これは空闲ページを異なるオーダ(order)の連続ブロックとして整理し、分割と結合によって効率的な割り当てと解放を実現します。buddy の上位には slab/slub(小オブジェクト用アロケータ)があり、下位には NUMA 対応の zone およびノードトポロジーが存在します。
NUMA アーキテクチャ
UMA (Uniform Memory Access):
全 CPU が全メモリにアクセスするレイテンシが同一
典型: 単ソケット、デスクトップ
NUMA (Non-Uniform Memory Access):
メモリは複数のノードに分割される
各 CPU にはローカルメモリがあり(高速アクセス)、
リモートメモリへのアクセスは遅い(30%〜40%遅延)
典型: 複数ソケットサーバー
確認方法: numactl --hardware
ls /sys/devices/system/node/
pg_data_t: NUMA ノード記述子
// include/linux/mmzone.h
typedef struct pglist_data pg_data_t;
Zone: 物理メモリの分区
Zone が必要な理由
Zone が存在する根本的な理由: ハードウェアの制約により、特定の物理メモリは
特定の目的には使用できないため
16-bit ISA DMA: 物理アドレス 0〜16MB しかアクセス不可
32-bit PCI DMA: 0〜4GB しかアクセス不可(IOMMU なしの場合)
32-bit カーネル: 896MB までしか直接マッピングできない(highmem)
64-bit: highmem は存在しないが、zone 構造は DMA/DMA32 の制約を維持
Zone の種類
// include/linux/mmzone.h
;
struct zone
// include/linux/mmzone.h
;
水位線(Watermarks)とメモリ圧力
// アロケーション時の watermark チェック:
// zone_watermark_ok(zone, order, mark, classzone_idx, alloc_flags)
// zone の空闲ページが order 分の割り当てを満たし、mark 以下にならないか計算
// 不十分の場合 → リクライムをトリガー(kswapd または direct reclaim)
Buddy Allocator
データ構造
// mm/page_alloc.c
// 各 zone の各 order ごとに free_area が存在
// free_area[order]: 2^order ページサイズの空闲ブロックのリスト
;
// 移行タイプ(断片化対策):
;
コアアルゴリズム
// アロケーション:
// alloc_pages(gfp_mask, order) → __alloc_pages()
// → get_page_from_freelist() // 適切な zone から割り当てを試みる
// → rmqueue() // buddy の free_area から取得
// → __rmqueue_smallest() // order 以上の最小空闲ブロックを検索
// → expand() // 見つかったブロックが必要より大きい場合 → 分割
// 余剰部分を buddy の free_area に戻す
// 例: order=1(2ページ = 8KB)を要求
// free_area[2] に 16KB の空闲がある場合 → expand():
// → order 0〜1 の最初の 8KB を呼び出し元に割り当て
// → 残りの 8KB を free_area[1] に格納
// 解放:
// free_pages(addr, order) → __free_pages()
// → __free_one_page()
// → buddy(相手側ブロック)も空闲か確認
// → buddy が空闲の場合 → order+1 のブロックに結合 → 再帰的に結合
// → buddy が空闲でない場合 → 対応する order の free_area に格納
// Buddy チェック:
// buddy_pfn = page_pfn ^ (1 << order) // XOR: buddy の PFN
// buddy も空闲で同じ order なら → 結合
GFP フラグ
// include/linux/gfp_types.h
// アロケーション動作の制御:
// Zone モディファイア:
// Watermark モディファイア:
// Reclaim モディファイア:
// 一般的な組み合わせ:
// 最も一般的: スリープ/リクライム/IO を許可
// スリープ不可: 割り込み/atomic コンテキスト用
// ユーザー空間での割り当て
// 待たない: 割り当て失敗時に即座に NULL を返す
Per-CPU Pages (PCP)
// mm/page_alloc.c
// 各 CPU には per-CPU の 1ページキャッシュ(struct per_cpu_pages)が存在
//
// なぜ必要か?
// 単一ページの割り当て(order 0)は最も一般的な操作
// 毎回 zone->lock(グローバルロック)を取得すると → ロック競合が深刻
// PCP は L1 キャッシュとして機能: ローカルにバッチでページをプリフェッチ
// → alloc: PCP にあれば → 直接取得(ロック不要)
// PCP が空の場合 → buddy からバッチ取得(zone lock を1回取得)
// → free: PCP が満杯でない場合 → PCP に戻す(ロック不要)
// PCP が満杯の場合 → buddy に戻す(zone lock を1回取得)
//
// 設計思想: バッチ処理によりロックオーバーヘッドを分散
struct page
// include/linux/mm_types.h
// 各物理ページには struct page が1つ対応(4KB ページに対して 64バイト → 約1.5%のオーバーヘッド)
;
struct page は過負荷のかかった構造体です——ページの用途(buddy 空闲、slab、匿名メモリ、page cache、THP compound)によって、同じフィールドが異なる意味を持ちます。カーネルは flags 内のビットを使用してこれを区別します。
Memory Hotplug
// mm/memory_hotplug.c
// 物理メモリの追加/削除:
// online: add_memory() → __add_memory() → page struct の初期化 → buddy への登録
// offline: remove_memory() → __remove_memory() → 使用中のページを移行 → buddy からの解除
// DIMM (NVDIMM/CXL):
// ZONE_DEVICE として構成可能(buddy 管理外)
// または通常のメモリ(ZONE_NORMAL)として構成し、buddy によって割り当て可能
デバッグ
# Buddy allocator の状態
# Node 0, zone Normal: 11 4 2 1 0 0 ...
# order 0: 11 blocks(4KB ページとして 44KB 空闲)
# order 1: 4 blocks (8KB チャンクとして 32KB 空闲)
# Zone 情報
# 含まれる情報: per-zone 水位線、空闲ページ、保護情報
# NUMA 統計
# メモリ断片化指数
# 1.000 = 完全に断片化しており、連続ページを割り当て不可
# 0.000 = 断片化なし
# トリガーされたリクライムの統計
|
参考と拡張
- カーネルドキュメント:
Documentation/mm/page_alloc.rst,Documentation/admin-guide/sysctl/vm.rst - LWN:
- "The design of the buddy allocator" (lwn.net/Articles/259832/)
- "Memory management APIs" シリーズ
- ソースファイル:
mm/page_alloc.c— buddy allocator(約7000行)include/linux/mmzone.h— zone, pg_data_t, free_areainclude/linux/mm_types.h— struct pageinclude/linux/gfp_types.h— GFP フラグ
キーワード: NUMA, Zone, Buddy Allocator, GFP_KERNEL, GFP_ATOMIC, struct page, watermark, kswapd, memory hotplug, compaction