ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Linux 5.15 文件系统知识图谱(VFS/页缓存/块层/f2fs 结构索引)

2026/8/4 12:49:07 拓冰建站 浏览量
Linux 5.15 文件系统知识图谱(VFS/页缓存/块层/f2fs 结构索引)

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_infof2fs.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_pagenode.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. 待深化/疑问区(学习过程中持续更新)

学习中发现的新概念、疑问、以及图谱待补充部分记在这里,成熟后并入正式章节。