graph LR
A["Ext2\n1993"] -->|"加日志"| B["Ext3\n2001"]
B -->|"加 Extent\n更大文件"| C["Ext4\n2008"]
A -.->|"无日志\n快但脆弱"| A1["❌"]
B -.->|"崩溃恢复"| B1["✅"]
C -.->|"1 EB\n16 TB 单文件"| C1["✅"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style A1 fill:#FFF9C4,stroke:#F9A825,color:#333
style B1 fill:#FFF9C4,stroke:#F9A825,color:#333
style C1 fill:#FFF9C4,stroke:#F9A825,color:#333
对比:
特性
Ext2
Ext3
Ext4
最大文件
2 TB
2 TB
16 TB
最大 FS
16 TB
16 TB
1 EB
日志
❌
✅
✅
Extent
❌
❌
✅
多块分配
❌
❌
✅
延迟分配
❌
❌
✅
在线碎片整理
❌
❌
✅
一、Ext4 磁盘布局
1.1 整体布局
graph TB
A["磁盘 / 分区"] --> B["引导块\n(1024 字节)"]
B --> C["块组 0"]
C --> D["块组 1"]
D --> E["..."]
E --> F["块组 N"]
C --> C1["Superblock\n(副本)"]
C --> C2["块组描述符表"]
C --> C3["数据位图"]
C --> C4["inode 位图"]
C --> C5["inode 表"]
C --> C6["数据块"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#E8D5F5,stroke:#CE93D8,color:#333
style D fill:#E8D5F5,stroke:#CE93D8,color:#333
style E fill:#E8D5F5,stroke:#CE93D8,color:#333
style F fill:#E8D5F5,stroke:#CE93D8,color:#333
style C1 fill:#FFF9C4,stroke:#F9A825,color:#333
style C2 fill:#FFF9C4,stroke:#F9A825,color:#333
style C3 fill:#FFF9C4,stroke:#F9A825,color:#333
style C4 fill:#FFF9C4,stroke:#F9A825,color:#333
style C5 fill:#FFF9C4,stroke:#F9A825,color:#333
style C6 fill:#FFF9C4,stroke:#F9A825,color:#333
1.2 块(Block)
基本单位——磁盘读写最小单位
大小:1 KB、2 KB、4 KB(默认)、8 KB
1.3 块组(Block Group)
graph LR
A["块组"] --> B["Super Block 副本"]
A --> C["Group Descriptors"]
A --> D["Block Bitmap"]
A --> E["Inode Bitmap"]
A --> F["Inode Table"]
A --> G["Data Blocks"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style F fill:#E8D5F5,stroke:#CE93D8,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
graph TB
A["inode (256 字节)"] --> B["i_mode\n文件类型+权限"]
A --> C["i_uid / i_gid\n用户组"]
A --> D["i_size\n文件大小"]
A --> E["i_atime / i_mtime / i_ctime\n时间戳"]
A --> F["i_blocks\n块数(512 字节单位)"]
A --> G["i_block[15]\n块指针"]
A --> H["i_generation\n文件代"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#E8D5F5,stroke:#CE93D8,color:#333
style G fill:#E8D5F5,stroke:#CE93D8,color:#333
style H fill:#FFF9C4,stroke:#F9A825,color:#333
graph TB
A["传统块指针"] --> B["i_block[12] 直接"]
B --> C["i_block[13] 一级间接"]
C --> D["i_block[14] 二级间接"]
D --> E["i_block[15] 三级间接"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#FFF9C4,stroke:#F9A825,color:#333
块指针的问题:
每个 i_block 表示一个块
1 GB 文件需要 25 万个 i_block 单元
不可能塞进 inode → 必须多级间接
Extent 优势:
一个 extent 表示连续块范围
1 GB 文件:128 KB extent 块 = 8192 个 extent
直接塞进 inode(最多 4 个 extent)
3.3 Extent 树
graph TB
A["inode"] --> B["ext4_extent_header\n(深度=1)"]
B --> C["extent 1\n[0-100)"]
B --> D["extent 2\n[100-200)"]
B --> E["extent 3\n[200-300)"]
B --> F["..."]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#FFF9C4,stroke:#F9A825,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333
深度 > 0 时 = 索引节点:
graph TB
A["inode"] --> B["header (depth=1)"]
B --> C["ext4_extent_idx 1"]
B --> D["ext4_extent_idx 2"]
B --> E["ext4_extent_idx 3"]
C --> F["叶子: extent 1"]
C --> G["叶子: extent 2"]
D --> H["叶子: extent 3"]
D --> I["叶子: extent 4"]
E --> J["叶子: extent 5"]
E --> K["叶子: extent 6"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#FFF9C4,stroke:#F9A825,color:#333
style F fill:#B5EAD7,stroke:#80CBC4,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
style H fill:#B5EAD7,stroke:#80CBC4,color:#333
style I fill:#B5EAD7,stroke:#80CBC4,color:#333
style J fill:#B5EAD7,stroke:#80CBC4,color:#333
style K fill:#B5EAD7,stroke:#80CBC4,color:#333
四、目录结构
4.1 目录实现:HTree
graph TB
A["目录 /home"] --> B["目录项 1\n. (本目录)"]
A --> C["目录项 2\n.. (上级)"]
A --> D["目录项 3\nuser → inode#100"]
A --> E["目录项 4\nadmin → inode#200"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#FFF9C4,stroke:#F9A825,color:#333
graph TB
A["目录 inode"] --> B["HTree 根"]
B --> C["分支 1 (a-h)"]
B --> D["分支 2 (i-p)"]
B --> E["分支 3 (q-z)"]
C --> F["线性目录项 a, b, c..."]
D --> G["线性目录项 i, j, k..."]
E --> H["线性目录项 q, r, s..."]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFF9C4,stroke:#F9A825,color:#333
style D fill:#FFF9C4,stroke:#F9A825,color:#333
style E fill:#FFF9C4,stroke:#F9A825,color:#333
style F fill:#B5EAD7,stroke:#80CBC4,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
style H fill:#B5EAD7,stroke:#80CBC4,color:#333
HTree 优势:
大目录查找从 O(n) → O(log n)
哈希索引
4 KB 块最多 1M 个目录项
五、Journaling(日志机制)
5.1 为什么需要日志?
graph LR
A["写入数据"] --> B["Ext2:直接写元数据\n崩溃 → 元数据不一致"]
C["写入数据"] --> D["Ext3/4:先写日志\n崩溃 → 重放日志"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#F48FB1,stroke:#FFB3C6,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#80CBC4,stroke:#B5EAD7,color:#333
5.2 日志模式
graph TB
A["日志模式"] --> B["journal\n完整日志"]
A --> C["ordered\n仅元数据日志"]
A --> D["writeback\n不日志"]
B --> B1["数据+元数据\n最安全最慢"]
C --> C1["元数据+数据块先写\n平衡"]
D --> D1["只元数据\n最快风险最大"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#B5EAD7,stroke:#80CBC4,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style B1 fill:#FFF9C4,stroke:#F9A825,color:#333
style C1 fill:#FFF9C4,stroke:#F9A825,color:#333
style D1 fill:#FFF9C4,stroke:#F9A825,color:#333
5.3 Journal 工作流
sequenceDiagram
participant App as 应用
participant JBD as JBD2 日志层
participant Disk as 磁盘
App->>JBD: write data + metadata
JBD->>Disk: 写入 journal(事务)
JBD->>Disk: checkpoint(写入最终位置)
Disk-->>JBD: ack
JBD-->>App: 完成
Note over Disk: 崩溃恢复时
Disk->>JBD: 重放未完成的事务
JBD->>Disk: checkpoint 完成
graph TB
A["Ext4 核心"] --> B["磁盘布局\n引导+块组"]
A --> C["Extent\n替代块指针"]
A --> D["HTree\n大目录索引"]
A --> E["Journaling\n崩溃恢复"]
A --> F["延迟分配\n性能优化"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333