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

Linux 5.15 文件系统知识图谱

所有路径/行号为本地实测(grep 定位),版本 5.15。


1. 总体分层架构

graph TD subgraph 用户态 U[用户进程<br/>open/read/write/mmap/io_uring] end subgraph 系统调用层 SYSCALL[系统调用入口<br/>fs/open.c fs/read_write.c fs/ioctl.c] end subgraph VFS核心层 VFS[VFS 虚拟文件系统<br/>fs/namei.c dcache.c inode.c super.c file.c] FS_TYPE[file_system_type 注册表<br/>fs/filesystems.c] end subgraph 具体文件系统 EXT4[ext4] F2FS[f2fs] EROFS[erofs] TMPFS[shmem/tmpfs] PROC[proc/sysfs/debugfs] end subgraph 通用基础设施 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/] end subgraph 块层 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] end subgraph 硬件 DRV[设备驱动<br/>virtio_blk/sd 等] end U --> SYSCALL SYSCALL --> VFS VFS --> FS_TYPE VFS --> EXT4 & F2FS & EROFS & TMPFS & PROC EXT4 & F2FS & EROFS --> PAGECACHE PAGECACHE --> WRITEBACK EXT4 --> IOMAP EXT4 --> JBD2 PAGECACHE --> BIO WRITEBACK --> BIO BIO --> BLK_MQ BLK_MQ --> SCHED SCHED --> GENHD GENHD --> DRV VFS -.-> NOTIFY

2. 核心数据结构关系

classDiagram class 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_type super_block --> dentry : s_root super_block --> inode : s_inodes dentry --> inode : d_inode dentry --> dentry : d_parent inode --> address_space : i_data file --> path : f_path file --> address_space : f_mapping path --> vfsmount : mnt path --> dentry : dentry vfsmount --> super_block : mnt_sb file ..> file_operations : f_op inode ..> inode_operations : i_op inode ..> file_operations : i_fop super_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 旅程

sequenceDiagram participant U as 用户进程 participant O as fs/open.c participant N as fs/namei.c participant D as fs/dcache.c participant I as fs/inode.c participant F as fs/ext4 U->>O: open("file", O_RDONLY) O->>O: do_sys_open → do_sys_openat2 O->>O: alloc_empty_file() 分配 struct file O->>N: path_openat(nd, ...) loop 每个路径分量 N->>N: link_path_walk / walk_component N->>D: d_lookup() dcache 查找 alt 缓存未命中 D-->>N: 未命中 N->>I: lookup_slow → inode ops->lookup() I->>F: ext4_lookup() F-->>I: 磁盘查找 inode I->>I: iget_locked() I-->>N: dentry + inode 关联 end end N->>O: vfs_open(nd->path) O->>O: do_dentry_open() → f_op->open() O-->>U: 返回 fd

4.2 read 旅程(页缓存未命中)

sequenceDiagram participant U as 用户进程 participant RW as fs/read_write.c participant EX as fs/ext4/file.c participant FM as mm/filemap.c participant RA as mm/readahead.c participant BIO as block/ participant D as 设备驱动 U->>RW: read(fd, buf, n) RW->>EX: vfs_read → ext4_file_read_iter EX->>FM: generic_file_read_iter → filemap_read FM->>FM: filemap_get_pages:xarray 查找页 alt 页缓存未命中 FM->>RA: page_cache_sync_readahead RA->>FM: ondemand_readahead 计算预读窗口 FM->>FM: readahead 页 → aops->readahead() FM->>BIO: submit_bio() 组装 bio BIO->>D: blk_mq_submit_bio → 硬件队列 D-->>BIO: IO 完成 BIO-->>FM: 页置 PG_uptodate, 解锁 end FM-->>RW: copy_page_to_iter 拷贝到用户 buf RW-->>U: 返回 n

4.3 write 旅程(延迟落盘)

sequenceDiagram participant U as 用户进程 participant RW as fs/read_write.c participant EX as fs/ext4/file.c participant FM as mm/filemap.c participant PW as mm/page-writeback.c participant WB as fs/fs-writeback.c participant BIO as block/ participant D as 设备驱动 U->>RW: write(fd, buf, n) RW->>EX: vfs_write → ext4_file_write_iter EX->>FM: iomap 写 → generic_perform_write FM->>FM: 写入页缓存页,置 PG_dirty FM->>PW: balance_dirty_pages_ratelimited PW-->>FM: 未超限 → 立即返回(write 完成) Note over FM: 后台内核线程 (flusher) WB->>PW: 周期性/超限触发 wb_start_background_writeback WB->>WB: writeback_sb_inodes → aops->writepages() WB->>BIO: 组装 bio,submit_bio() BIO->>D: 下发设备 D-->>BIO: 完成 BIO-->>FM: 页清 PG_writeback → PG_clean Note 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. 待深化/疑问区(学习过程中持续更新)

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

posted @ 2026-08-04 12:28  zenwater  阅读(4)  评论(0)    收藏  举报