4 分で読了 #linux-kernel #file-system
このページの目次

ファイルシステムの実装

対応範囲: ext4 (extent tree/jbd2/delayed allocation) → XFS (AG/B+tree/reflink) → Btrfs (COW/subvolume/snapshot/checksum) → bcachefs (6.7+) カーネルバージョン: 2.6 ~ 6.x

概要

Linux がサポートするローカルファイルシステムは、それぞれ異なる設計思想を持っています:

  • ext4: 安定性最優先。ジャーナル + 遅延割り当て。汎用シーンに適している
  • XFS: 大規模な並列 I/O。割り当てグループ (AG) 設計。大規模ファイルや高同時接続に適している
  • Btrfs: COW + チェックサム + スナップショット。NAS やデータの完全性が保証されるシーンに適している
  • bcachefs: 新世代の COW ファイルシステム (6.7+)。copygc + 複数デバイス + 圧縮

ext4: 安定した汎用ソリューション

Extent Tree

// ext4 は間接ブロック (ext2/3 の間接/二重間接/三重間接) の代わりに
// extent tree を使用します
// 1つの extent は「ファイルオフセット X ~ X+LEN がディスクブロック Y ~ Y+LEN にマッピングされる」
// を表します

struct ext4_extent {
    __le32  ee_block;       // 論理ブロック番号 (ファイル内オフセット)
    __le16  ee_len;         // この extent がカバーするブロック数
    __le16  ee_start_hi;    // 物理ブロック番号の上位16ビット
    __le32  ee_start_lo;    // 物理ブロック番号の下位32ビット (48ビットブロック番号)
};

// extent tree は B-tree です (inode内または追加のブロック内に格納)
// ext4_inode->i_data[60] = 4つの ext4_extent + 1つのヘッダ
//   → 最初の4つの extent は inode 内に直接存在します (追加のブロック読み込み不要!)
//   → 4つを超えると → B-tree ノードに分割されます

// なぜ間接ブロックよりも優れているのか?
//   間接ブロック: N個のブロック → O(N) の検索 (ブロックごとに1つのポインタ)
//   extent:      N個のブロック → O(1) の extent カバー (連続ブロックは1つのレコードで処理)
//   断片化されたファイル: extent tree は複数のノードを持つ可能性がある → それでも O(log N)

jbd2: ジャーナル

// ext4 における書き込み操作のパス:
//   1. データをディスクに書き込む (data)
//   2. メタデータの変更をジャーナルに書き込む (metadata)
//   3. ジャーナルコミットが完了したことをマーク
//   4. チェックポイント: ジャーナル内のメタデータを実際の位置に書き戻す
//
// マウント時:
//   ジャーナルをチェック → チェックポイントされていないがコミット済みのトランザクションをリプレイ
//   → 一貫性を回復 (fsck はほとんど不要)

// data=ordered (デフォルト):
//   データを先に書き込む → 次にジャーナルに書き込む → データがメタデータより先に見えることはない
//   → クラッシュ後もファイルデータは完全 (メタデータは不完全でも、ゴミを指すことはない)

// data=writeback:
//   データは順序不同 → より高速だが、クラッシュ後に古いデータが見える可能性がある

Delayed Allocation (遅延割り当て)

ext4 の遅延割り当て:
  書き込み操作 → まず page cache に蓄積 → ディスクブロックの即時割り当てを行わない
  → writeback 時 (数秒間蓄積後) → 大きな連続した extent を割り当て
  → 断片化を減らし、シーケンシャル書き込み性能を向上

代償:
  1. write() の成功はディスクスペースが割り当てられたことを意味しない → ENOSPC は書き戻し時に発生
  2. クラッシュ時に失われるデータが増える (page cache 内のダーティページで、まだディスクブロックが割り当てられていない)

fallocate を使用して事前にスペースを割り当てれば、ENOSPC の遅延を回避できる

XFS: 大規模並列処理

割り当てグループ (Allocation Group)

XFS はファイルシステムを複数の AG (Allocation Group) に分割する
各 AG は独自の inode と空き空間 B+tree を独立して管理する
  → スレッド並列で割り当て可能 → グローバルロックを回避
  → 高同時接続シーン (例: マルチスレッドデータベース) では ext4 より優れている

AGs: [AG 0] [AG 1] [AG 2] ... [AG N]
  各 AG: 独自の superblock コピー、inode B+tree、空き空間 B+tree を持つ

B+tree メタデータ

// XFS のメタデータのほとんどは B+tree として整理されている:
//   inode B+tree:    inode 番号 → inode データ
//   空き空間 B+tree: オフセット → 空き extent
//   extent B+tree:     ファイルオフセット → ディスク extent (inodeごと)
//
// B+tree の汎用実装: fs/xfs/libxfs/xfs_btree.c
XFS 5.x+ は reflink (データブロックの共有、COW) をサポート:
  cp --reflink=always a b
    → a と b は同じデータ extent を共有
    → どちらか片方が変更 → COW → 変更されたブロックをコピー → 各自独立

Btrfs との違い:
  XFS の reflink は extent レベルの COW — ファイル全体をコピーしない
  Btrfs はファイルレベルの COW — 各スナップショット/サブボリュームが独立して書き込む

Btrfs: COW とデータの完全性

COW アーキテクチャ

Btrfs の COW:
  すべての書き込み操作は新しい位置に書き込む → 旧データを上書きしない
  1. 空きスペースに新しい extent を割り当てる
  2. 新しいデータを書き込む
  3. メタデータ B-tree を更新して新しい extent を指すようにする
  4. 旧 extent は空き状態になる

  クラッシュ安全性:
    COW であるため → 旧データは常に元の位置に残る
    → ジャーナルは不要 (スナップショットと reflink はネイティブにサポート)
    → トランザクションコミット = superblock の新しいルートへの書き込み

  代償:
    断片化 (頻繁な COW → 論理的に連続したデータが物理的に分散)
    書き込み増幅 (小さな変更 → 全体 extent のコピー)

主要機能

スナップショット:
  btrfs subvolume snapshot . snapshots/$(date +%Y%m%d)
    → O(1) で作成、データをコピーしない
    → COW 保証: 元のサブボリュームを変更してもスナップショットには影響しない

サブボリューム:
  独立した名前空間。個別にマウント可能
  ZFS の dataset に類似した概念

チェックサム:
  各データブロックとメタデータブロックにはチェックサム (crc32c) がある
  読み込み時に自動検証 → ビットロット/サイレント破損を検出
  RAID1 プロファイルと連携: 破損を検出 → ミラーから自動修復

圧縮:
  mount -o compress=zstd → extent 単位で圧縮 (zlib/lzo/zstd)

B-tree フォレスト

Btrfs のメタデータ構成:
  複数の B-tree (B-tree コードを共有):
    Root tree    → 他のツリーのルートへのポインタ
    FS tree      → ファイル/ディレクトリの inode (サブボリュームごとに1つ)
    Extent tree  → extent の割り当て状況
    Chunk tree   → チャンクグループから物理ブロックへのマッピング
    Device tree  → デバイス情報
    Csum tree    → extent のチェックサム

bcachefs: 新世代 (6.7+)

設計目標: 「btrfs の機能 + ext4/xfs の性能 + ZFS のデータ信頼性」
作者: Kent Overstreet (bcache の原作者)

主要機能:
  - COW が中心。ジャーナルはオプション
  - 複数デバイス: 異なるディスクが異なる役割を担える
    (SSD をフロント/キャッシュ用、HDD をコールドストレージ用)
  - 内蔵圧縮 (zstd/lz4/gzip) + チェックサム
  - 内蔵暗号化 (extent 単位)
  - サブボリューム / スナップショット
  - ディスクフォーマットは安定 (mainline 6.7 にマージ済み)

Btrfs との違い:
  bcachefs は設計当初から複数デバイスの階層化を考慮
  Btrfs は RAID をファイルシステムレベルで構成するが、bcachefs はより柔軟

ファイルシステムの選択

特性ext4XFSBtrfsbcachefs
最大ボリューム1EB8EB16EB8EB
最大ファイル16TB8EB16EB8EB
COWなしreflink のみありあり
スナップショットなしなしありあり
内蔵圧縮なしなしありあり
内蔵 RAIDなしなしありあり
チェックサムジャーナルのみジャーナルのみメタデータ+データメタデータ+データ
適したシーン汎用大ファイル/データベースNAS実験用/複数デバイス

参考と拡張

  • カーネルドキュメント⁠: Documentation/filesystems/ext4/, Documentation/filesystems/xfs/, Documentation/filesystems/btrfs.rst
  • LWN:
    • "bcachefs merged" (lwn.net/Articles/947587/)
    • "Btrfs: design and implementation" シリーズ
  • ソースコード⁠: fs/ext4/, fs/xfs/, fs/btrfs/, fs/bcachefs/

キーワード: ext4, extent tree, jbd2, delayed allocation, XFS, allocation group, B+tree, Btrfs, COW, checksum, bcachefs