Linux 5.15 文件系统知识图谱
源码根目录:z:\t6w\common\common14-5.15\common
所有路径/行号为本地实测(grep 定位),版本 5.15。
1. 总体分层架构
graph TDsubgraph 用户态U[用户进程<br/>open/read/write/mmap/io_uring]endsubgraph 系统调用层SYSCALL[系统调用入口<br/>fs/open.c fs/read_write.c fs/ioctl.c]endsubgraph VFS核心层VFS[VFS 虚拟文件系统<br/>fs/namei.c dcache.c inode.c super.c file.c]FS_TYPE[file_system_type 注册表<br/>fs/filesystems.c]endsubgraph 具体文件系统EXT4[ext4]F2FS[f2fs]EROFS[erofs]TMPFS[shmem/tmpfs]PROC[proc/sysfs/debugfs]endsubgraph 通用基础设施PAGECACHE[页缓存<br/>mm/filemap.c]WRITEBACK[回写<br/>mm/page-writeback.c fs/fs-writeback.c]IOMAP[iomap 映射<br/>fs/iomap/]JBD2[jbd2 日志]NOTIFY[fsnotify/inotify<br/>fs/notify/]endsubgraph 块层BIO[bio<br/>block/bio.c blk_types.h:221]BLK_MQ[blk-mq 多队列<br/>block/blk-mq.c]SCHED[IO 调度器<br/>mq-deadline/kyber/bfq]GENHD[genhd 磁盘<br/>block/genhd.c]endsubgraph 硬件DRV[设备驱动<br/>virtio_blk/sd 等]endU --> SYSCALLSYSCALL --> VFSVFS --> FS_TYPEVFS --> EXT4 & F2FS & EROFS & TMPFS & PROCEXT4 & F2FS & EROFS --> PAGECACHEPAGECACHE --> WRITEBACKEXT4 --> IOMAPEXT4 --> JBD2PAGECACHE --> BIOWRITEBACK --> BIOBIO --> BLK_MQBLK_MQ --> SCHEDSCHED --> GENHDGENHD --> DRVVFS -.-> NOTIFY
2. 核心数据结构关系
classDiagramclass super_block {+s_fs_info+s_root : dentry+s_bdev+s_op : super_operations+s_mounts+s_type : file_system_type}class file_system_type {+name+fs_flags+mount()/init_fs_context()+kill_sb()}class dentry {+d_parent+d_name : qstr+d_inode+d_op : dentry_operations+d_sb : super_block+d_hash}class inode {+i_mode+i_op : inode_operations+i_fop : file_operations+i_sb : super_block+i_mapping : address_space+i_data : address_space+i_ino}class address_space {+host : inode+i_pages : xarray+a_ops : address_space_operations+nrpages}class file {+f_path : path+f_op : file_operations+f_mapping : address_space+f_mode+f_pos+private_data}class path {+mnt : vfsmount+dentry : dentry}class vfsmount {+mnt_root : dentry+mnt_sb : super_block+mnt_parent / mnt_mountpoint}class file_operations {+open()+read_iter()+write_iter()+mmap()+release()+fsync()}class inode_operations {+lookup()+create()+link()+unlink()+rename()+getattr()}class super_operations {+alloc_inode()+write_inode()+evict_inode()+sync_fs()+statfs()}super_block --> file_system_type : s_typesuper_block --> dentry : s_rootsuper_block --> inode : s_inodesdentry --> inode : d_inodedentry --> dentry : d_parentinode --> address_space : i_datafile --> path : f_pathfile --> address_space : f_mappingpath --> vfsmount : mntpath --> dentry : dentryvfsmount --> super_block : mnt_sbfile ..> file_operations : f_opinode ..> inode_operations : i_opinode ..> file_operations : i_fopsuper_block ..> super_operations : s_op
结构体定义位置(5.15 本地实测):
| 结构体 |
头文件:行号 |
struct address_space |
include/linux/fs.h:468 |
struct inode |
include/linux/fs.h:642 |
struct file |
include/linux/fs.h:987 |
struct super_block |
include/linux/fs.h:1516 |
struct file_operations |
include/linux/fs.h:2103 |
struct inode_operations |
include/linux/fs.h:2150 |
struct super_operations |
include/linux/fs.h:2237 |
struct file_system_type |
include/linux/fs.h:2566 |
struct dentry |
include/linux/dcache.h:92 |
struct path |
include/linux/path.h:8 |
struct vfsmount |
include/linux/mount.h:72 |
struct bio |
include/linux/blk_types.h:221 |
3. 关键函数索引(路径:行号)
3.1 VFS / open 路径
| 函数 |
位置 |
职责 |
do_sys_open |
fs/open.c:1247 |
open 系统调用核心入口 |
do_sys_openat2 |
fs/open.c:1218 |
openat2 参数处理 |
alloc_empty_file |
fs/open.c:972 |
分配 struct file |
path_openat |
fs/namei.c:3730 |
路径解析主流程 |
d_lookup |
fs/dcache.c:2372 |
dcache 哈希查找 dentry |
__d_lookup |
fs/dcache.c:2402 |
无 rename_lock 的快速查找 |
iget_locked |
fs/inode.c:1289 |
inode 缓存获取(命中或新建) |
vfs_get_tree |
fs/super.c:1518 |
挂载时实例化 super_block |
submit_bio |
block/blk-core.c:1051 |
通用 bio 提交入口 |
blk_mq_submit_bio |
block/blk-mq.c:2196 |
blk-mq 队列提交 |
3.2 读路径 / 页缓存
| 函数 |
位置 |
职责 |
filemap_fault |
mm/filemap.c:3065 |
mmap 缺页 → 读页缓存/发起 IO |
do_sync_mmap_readahead |
mm/filemap.c:2966 |
同步 mmap 预读 |
do_async_mmap_readahead |
mm/filemap.c:3016 |
异步 mmap 预读 |
filemap_readahead |
mm/filemap.c:2529 |
常规 read 预读入口 |
ondemand_readahead |
mm/readahead.c:453 |
按需预读算法 |
ksys_readahead |
mm/readahead.c:628 |
readahead 系统调用 |
block_read_full_page |
fs/buffer.c:2312 |
buffer-head 读满一页 |
bdev_read_page |
block/bdev.c:321 |
块设备单页读 |
truncate_inode_pages |
mm/truncate.c:423 |
截断/释放页缓存 |
end_page_writeback |
mm/filemap.c:1585 |
回写结束清标志 |
3.3 写路径 / 回写
| 函数 |
位置 |
职责 |
balance_dirty_pages |
mm/page-writeback.c:1582 |
脏页超限时同步节流 |
balance_dirty_pages_ratelimited |
mm/page-writeback.c:1900 |
限速版脏页检查 |
wb_start_background_writeback |
mm/page-writeback.c:1686 |
触发后台回写 |
wait_on_page_writeback |
mm/page-writeback.c:2905 |
等待某页回写完成 |
tag_pages_for_writeback |
mm/page-writeback.c:2146 |
标记回写页范围 |
3.4 关键文件地图(fs/ 顶层)
| 文件 |
职责 |
fs/namei.c |
路径解析(walk/lookup)—— 最复杂的核心 |
fs/dcache.c |
dentry 缓存:分配/哈希/回收/引用计数 |
fs/inode.c |
inode 缓存与生命周期 |
fs/super.c |
super_block 生命周期、sget、挂载辅助 |
fs/file.c / fs/file_table.c |
struct file 与 fd 表管理 |
fs/open.c |
open/close/dup 系统调用实现 |
fs/read_write.c |
read/write/llseek 实现 |
fs/fs_context.c |
新版 mount API 上下文 |
fs/fsopen.c |
fsopen/fsconfig/fsmount 系统调用 |
fs/namespace.c |
挂载命名空间、挂载点树 |
fs/pnode.c |
挂载传播(shared/slave 等) |
fs/mount.h(include/linux/mount.h) |
vfsmount 结构 |
fs/buffer.c |
buffer-head 层(传统块 IO) |
fs/mpage.c |
通用页→bio 组装(mpage_*) |
fs/direct-io.c |
老式 DIO 实现 |
fs/seq_file.c |
/proc 顺序文件基础设施 |
fs/libfs.c |
简单文件系统通用助手 |
fs/fs-writeback.c |
后台回写核心 |
fs/locks.c |
POSIX/FLOCK 文件锁 |
fs/exec.c |
execve 文件装载 |
fs/xattr.c |
扩展属性 |
fs/ioctl.c |
通用 ioctl 框架 |
fs/aio.c / fs/io_uring.c |
异步 IO |
fs/splice.c |
splice/tee 零拷贝 |
fs/notify/ |
inotify/dnotify/fanotify |
fs/iomap/ |
现代块映射与 IO 基础设施 |
fs/ext4/ |
ext4 文件系统 |
fs/jbd2/ |
ext4 日志 |
fs/f2fs/ / fs/erofs/ / fs/squashfs/ |
其他主线文件系统 |
fs/overlayfs/ |
overlay 文件系统 |
3.5 块层文件地图(block/)
| 文件 |
职责 |
block/bio.c |
bio 分配/合并/迭代 |
block/blk-core.c |
submit_bio 总入口、通用请求处理 |
block/blk-mq.c |
多队列核心:映射/插桩/完成 |
block/blk-mq-sched.c |
调度器与 mq 交互 |
block/blk-merge.c |
请求合并 |
block/blk-flush.c |
刷新/FUA 语义 |
block/elevator.c |
调度器框架 |
block/mq-deadline.c |
deadline 调度器 |
block/kyber-iosched.c |
Kyber 调度器 |
block/bfq-iosched.c |
BFQ 调度器 |
block/genhd.c |
通用磁盘管理(分区、事件) |
block/bdev.c |
块设备文件操作 |
block/blk-cgroup.c |
blkio cgroup |
block/blk-throttle.c |
节流限速 |
block/blk-wbt.c |
写带宽节流 |
block/blk-iocost.c |
IO cost 控制 |
3.6 内存管理相关(mm/)
| 文件 |
职责 |
mm/filemap.c |
页缓存核心:查找/预读/回写标记/mmap fault |
mm/readahead.c |
预读算法 |
mm/page-writeback.c |
脏页管理与回写触发 |
mm/mmap.c |
内存映射区管理 |
mm/truncate.c |
inode 页截断/释放 |
mm/shmem.c |
tmpfs/shmem 实现 |
mm/vmscan.c |
页面回收(LRU) |
mm/swapfile.c / mm/swap_state.c |
swap 文件与 swap cache |
mm/memcontrol.c |
内存 cgroup |
4. 核心调用链时序
4.1 open 旅程
sequenceDiagramparticipant U as 用户进程participant O as fs/open.cparticipant N as fs/namei.cparticipant D as fs/dcache.cparticipant I as fs/inode.cparticipant F as fs/ext4U->>O: open("file", O_RDONLY)O->>O: do_sys_open → do_sys_openat2O->>O: alloc_empty_file() 分配 struct fileO->>N: path_openat(nd, ...)loop 每个路径分量N->>N: link_path_walk / walk_componentN->>D: d_lookup() dcache 查找alt 缓存未命中D-->>N: 未命中N->>I: lookup_slow → inode ops->lookup()I->>F: ext4_lookup()F-->>I: 磁盘查找 inodeI->>I: iget_locked()I-->>N: dentry + inode 关联endendN->>O: vfs_open(nd->path)O->>O: do_dentry_open() → f_op->open()O-->>U: 返回 fd
4.2 read 旅程(页缓存未命中)
sequenceDiagramparticipant U as 用户进程participant RW as fs/read_write.cparticipant EX as fs/ext4/file.cparticipant FM as mm/filemap.cparticipant RA as mm/readahead.cparticipant BIO as block/participant D as 设备驱动U->>RW: read(fd, buf, n)RW->>EX: vfs_read → ext4_file_read_iterEX->>FM: generic_file_read_iter → filemap_readFM->>FM: filemap_get_pages:xarray 查找页alt 页缓存未命中FM->>RA: page_cache_sync_readaheadRA->>FM: ondemand_readahead 计算预读窗口FM->>FM: readahead 页 → aops->readahead()FM->>BIO: submit_bio() 组装 bioBIO->>D: blk_mq_submit_bio → 硬件队列D-->>BIO: IO 完成BIO-->>FM: 页置 PG_uptodate, 解锁endFM-->>RW: copy_page_to_iter 拷贝到用户 bufRW-->>U: 返回 n
4.3 write 旅程(延迟落盘)
sequenceDiagramparticipant U as 用户进程participant RW as fs/read_write.cparticipant EX as fs/ext4/file.cparticipant FM as mm/filemap.cparticipant PW as mm/page-writeback.cparticipant WB as fs/fs-writeback.cparticipant BIO as block/participant D as 设备驱动U->>RW: write(fd, buf, n)RW->>EX: vfs_write → ext4_file_write_iterEX->>FM: iomap 写 → generic_perform_writeFM->>FM: 写入页缓存页,置 PG_dirtyFM->>PW: balance_dirty_pages_ratelimitedPW-->>FM: 未超限 → 立即返回(write 完成)Note over FM: 后台内核线程 (flusher)WB->>PW: 周期性/超限触发 wb_start_background_writebackWB->>WB: writeback_sb_inodes → aops->writepages()WB->>BIO: 组装 bio,submit_bio()BIO->>D: 下发设备D-->>BIO: 完成BIO-->>FM: 页清 PG_writeback → PG_cleanNote over U: 若用户调用 fsync/fdatasync,则同步等待上述流程
5. 关键机制备忘
| 机制 |
一句话 |
关键位置 |
| RCU 路径查找 |
LOOKUP_RCU 无锁读路径,失败退化为走锁 |
fs/namei.c |
| dcache 哈希 |
dentry 按 (parent, name) 哈希快速命中 |
fs/dcache.c |
| inode 缓存 |
iget_locked 按 (sb, ino) 查找,未命中则新建 |
fs/inode.c:1289 |
| 页缓存 |
以 4KB page/folio 为单位的 xarray 树 |
mm/filemap.c |
| 预读 |
on-demand + 顺序检测,窗口倍增/减半 |
mm/readahead.c:453 |
| 脏页节流 |
dirty_ratio/dirty_background_ratio 双重阈值 |
mm/page-writeback.c:1582 |
| 后台回写 |
flusher 线程周期写回(dirty_writeback_centisecs) |
fs/fs-writeback.c |
| 日志 (jbd2) |
ext4 元数据写前先记日志,保证崩溃一致性 |
fs/jbd2/ |
| delalloc |
延迟块分配,攒够再落盘,提升顺序性 |
fs/ext4/inode.c |
| blk-mq |
per-CPU 软件队列 + 多硬件队列,避免全局锁 |
block/blk-mq.c |
| bio |
块 IO 基本单元,bi_io_vec 段列表 |
include/linux/blk_types.h:221 |
6. f2fs 文件系统专题
6.1 定位与背景
- F2FS = Flash-Friendly File System,三星(Jaegeuk Kim)2012 年设计,Linux 3.8 合入主线。
- 专为 NAND 闪存(SSD/eMMC/UFS/SD)设计,基于 LFS(Log-structured File System) 思想:所有修改顺序追加写入,旧块作废待 GC。
- 权威文档:
Documentation/filesystems/f2fs.rst(本地 5.15 树)。
- 工具链(外部):mkfs.f2fs / fsck.f2fs / dump.f2fs(git.kernel.org f2fs-tools)。
6.2 写模型对比(核心)
|
f2fs(异地更新) |
ext4(原地更新) |
| 改数据块 |
写新块,旧块作废 |
原位置覆盖 |
| 块指针 |
变化 → 需索引更新 |
地址不变 |
| 索引稳定性 |
漂移(wandering tree) |
稳定 |
| 一致性机制 |
checkpoint 快照 + NAT + 回滚恢复 |
jbd2 日志(先记操作再落盘) |
| 介质适配 |
闪存(顺序写强项) |
磁盘(随机写代价小) |
6.3 概念澄清:Journaling vs LFS
- Journaling(日志记录):元数据操作先写"日记本"再落盘 → ext4/XFS/NTFS,崩溃重放日志。ext4 有 journal,但不是 LFS。
- Log-structured(日志结构):整个文件系统就是一个顺序日志,所有写追加 → f2fs/NILFS2/JFFS2/UBIFS。
- 中文"日志"一词同时翻译两个概念,是常见混淆源。
6.4 LFS 两大难题与 f2fs 对策
| LFS 难题 |
问题描述 |
f2fs 对策 |
| Wandering tree(游走树) |
异地更新 → 数据块搬家 → 指针块级级重写 → inode → inode map → checkpoint 递归传播,写放大严重 |
NAT(Node Address Table):node 统一逻辑编号,父子指针指向 nid,经 NAT 间接定位物理块,切断传播链 |
| Cleaning overhead(清理开销) |
失效块散布全盘,GC 回收延迟高、搬移多 |
后台 GC + greedy/cost-benefit 选段 + 多头部日志冷热分离 + SSR + adaptive logging |
Wandering tree 5 步传播链(对照 ext4 逐条为何不成立):
数据 A → A' → 直接指针块改 → 间接指针块改 → inode 改 → inode map 改 → checkpoint 改
ext4:原地覆盖 → 指针不变 → 索引静态 → inode 固定槽位 → 无 inode map(号直定位)→ 无 checkpoint(靠 jbd2 日志)
6.5 NAT 核心机制
- Node:f2fs 术语,统一指 inode 和各级指针块。
- NAT:全盘唯一映射表
nid → node 块物理地址。
- 效果:数据更新只改一个 NAT 表项(O(1)),而非整棵索引树(O(深度))。
- 代价:NAT 需维护、需 checkpoint 落盘、占磁盘空间;崩溃恢复依赖 NAT 与 node 树一致(
fs/f2fs/recovery.c)。
本地源码锚点(fs/f2fs/,5.15):
| 组件 |
位置 |
作用 |
| Node Manager |
f2fs.h:921 struct f2fs_nm_info,f2fs.h:1952 NM_I() |
内存 NAT 缓存、nid 分配 |
| NAT 脏表项聚合 |
node.c:238 __grab_nat_entry_set |
脏 NAT 按 set 分组 |
| 查询 node 地址 |
node.c:546 f2fs_get_node_info() |
核心:内存缓存 → 磁盘 NAT 块两级查找 |
| 读 node 页 |
node.c:1426 __get_node_page → node.c:1488 f2fs_get_node_page |
按 nid 经 NAT 读 node 块 |
| NAT 落盘 |
node.c:3003 __flush_nat_entry_set |
checkpoint 时批量写回 |
文件地图:
| 文件 |
职责 |
super.c |
挂载/超级块(f2fs_fill_super) |
segment.c |
核心:segment 管理、分配器、日志头部 |
gc.c |
垃圾回收(选段+搬移) |
node.c |
NAT 表、node 块管理 |
checkpoint.c / recovery.c |
崩溃一致性 / roll-forward 恢复 |
data.c |
数据块读写主路径(页缓存对接) |
extent_cache.c / compress.c / inline.c |
扩展区缓存 / 压缩 / inline data/xattr/dentry |
dir.c / namei.c |
目录与名字解析 |
debug.c / sysfs.c / iostat.c |
调试与统计 |
6.6 与 ext4 对比总结
| 维度 |
ext4 |
f2fs |
| 数据写 |
随机覆盖,地址固定 |
顺序追加,地址漂移 |
| 闪存写放大 |
大(小块随机写→FTL 读改写) |
小(大块顺序写→匹配 FTL) |
| 冷热数据 |
不区分 |
冷热分离多日志 |
| 空间管理 |
块位图 + extent |
segment 化 + GC 整理 |
| 一致性 |
jbd2 日志 |
checkpoint + NAT |
| GC |
无 |
有(后台化、策略化) |
| 擅长 IO |
随机写/读平衡(磁盘) |
顺序写(闪存) |
6.7 与经典 LFS 对比(第三代 vs 前两代)
| 经典 LFS 缺陷 |
前代表现 |
f2fs 对策 |
| Wandering tree |
索引级联重写 |
NAT 间接层切断 |
| Cleaning overhead |
回收长延迟 |
多日志 + 冷热分离 |
| GC 卡顿 |
前台 GC 阻塞 |
后台 GC + gc_merge |
| segment 浪费 |
碎片化 |
SSR + 自适应分配 |
| 介质假设 |
JFFS2 面向裸 NAND |
面向带 FTL 的闪存 |
| 挂载/恢复 |
扫描全盘慢 |
checkpoint 快照快恢复 |
| 现代特性 |
缺失 |
压缩 / fs-verity / casefold / inline |
6.8 为什么 UFS 上 f2fs 优于 ext4
UFS 硬件特性:带 FTL 的 NAND;顺序写带宽是随机写 3~10 倍;擦除-再写(物理写放大);PE 寿命有限。
- ext4:应用随机小写 → 原地覆盖 → FTL 读改写整块 → 写放大 3~10 倍 → 性能差、磨损快。
- f2fs:回写攒成顺序大块 → 匹配 UFS 顺序写通道;冷热分离;discard/TRIM 及时释放 → 随机写场景性能提升约 2~3 倍、磨损显著降低。
- 实证:Pixel/三星旗舰等新机 data 分区默认 f2fs(Android 官方建议);三星 2012 论文《f2fs: A New File System for Flash Storage》实测随机写约 2 倍提升。
边界(诚实评估):大文件顺序读、服务端长时间随机读场景两者接近;f2fs 需定期 GC,极端碎片下前台 GC 仍可能感知卡顿(有 gc_merge/disable_roll_forward 等调优项)。
6.9 Android 文件系统演进史(介质-文件系统耦合)
裸 NAND 时代 eMMC+FTL 时代 UFS 时代
YAFFS2 (LFS) → ext4 (原地更新) → f2fs (协作式)
2008~2011 2011~2016 2016~至今
| 时代 |
介质 |
文件系统 |
逻辑 |
| Android 1.0~2.2 |
裸 NAND(MTD,无 FTL) |
YAFFS2(LFS) |
闪存管理自己做,LFS 是唯一合理选择 |
| Android 2.3~6 |
eMMC(内置 FTL) |
ext4 |
FTL 把闪存伪装成块设备,LFS 价值被吸收,ext4 成熟稳定成最优解 |
| Android 7+ |
eMMC/UFS |
f2fs(默认渐增) |
FTL 兜不住随机小写(写放大 3~10x),f2fs 在 FS 层攒顺序写,与 FTL 分工 |
YAFFS2 为何被弃:单线程、无缓存、随机读性能差;面向裸 NAND,在 eMMC 上叠 LFS = 双重 GC、双重写放大;工具链差、PC 不可挂载。
当时为何不选其他 LFS:
| 候选 |
原因 |
| JFFS2 / UBIFS |
面向裸 NAND+MTD,eMMC 有 FTL,无意义 |
| NILFS2 |
HDD 向 LFS,GC 复杂、不稳定、生态差 |
| LogFS 等 |
不成熟 |
| f2fs |
2012 年才发布(Linux 3.8),换 ext4 时(2011)尚不存在 |
关键结论:ext4 时代是"FTL 兜底"——FTL 固件替文件系统消化闪存问题;f2fs 时代是"FS 与 FTL 分工协作"——文件系统层随机写顺序化 + 冷热分离 + 对齐 FTL 操作单位。
6.10 挂载关键选项(f2fs.rst)
| 选项 |
作用 |
background_gc |
后台 GC 开关(on/off/sync,默认 on) |
gc_merge |
后台 GC 线程吞掉前台请求,消除卡顿 |
disable_roll_forward |
关闭回滚恢复 |
discard/nodiscard |
段清理时发 TRIM 命令 |
active_logs |
活跃日志数(2/4/6,默认 6) |
inline_xattr / inline_dentry |
内联 xattr / dentry |
nouser_xattr / noacl |
关闭用户 xattr / ACL |
7. 待深化/疑问区(学习过程中持续更新)
学习中发现的新概念、疑问、以及图谱待补充部分记在这里,成熟后并入正式章节。