Linux 存储与文件系统深度剖析系列(一):总览与架构
系列介绍
欢迎来到 Linux 存储与文件系统深度剖析系列!本系列将从代码级别深入探讨 Linux 内核中的存储子系统和文件系统实现,基于 Linux 6.4-rc1 内核源码进行分析。
系列文章目录
- 总览与架构(本文)
- VFS 虚拟文件系统层深度剖析
- 块设备层(Block Layer)详解
- 页缓存(Page Cache)与缓冲区缓存机制
- Ext4 文件系统源码分析
- Btrfs 文件系统核心原理
- XFS 文件系统实现细节
- IO 调度器深度剖析
- 直接 IO 与异步 IO 实现
- 文件系统性能优化与调试
Linux 存储栈整体架构
Linux 存储栈是一个复杂的分层架构,从用户空间到物理硬件,主要包含以下几层:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| ┌─────────────────────────────────────────────┐ │ 用户空间应用 │ │ (read, write, open, close, mmap...) │ └─────────────────┬───────────────────────────┘ │ System Call Interface ┌─────────────────▼───────────────────────────┐ │ VFS (Virtual Filesystem Switch) │ │ - struct inode, dentry, file, super │ │ - 通用文件操作接口 │ └─────────────────┬───────────────────────────┘ │ ┌───────────┼───────────┐ │ │ │ ┌─────▼────┐ ┌───▼────┐ ┌───▼────┐ │ Ext4 │ │ Btrfs │ │ XFS │ ... (具体文件系统) └─────┬────┘ └───┬────┘ └───┬────┘ │ │ │ └──────────┼──────────┘ │ ┌────────────────▼────────────────────────────┐ │ Page Cache / Buffer Cache │ │ (struct address_space, struct page) │ └────────────────┬────────────────────────────┘ │ ┌────────────────▼────────────────────────────┐ │ Block Layer (块设备层) │ │ - struct bio, struct request │ │ - 块设备通用操作 │ └────────────────┬────────────────────────────┘ │ ┌────────────────▼────────────────────────────┐ │ IO Scheduler (IO调度器) │ │ - mq-deadline, BFQ, Kyber │ └────────────────┬────────────────────────────┘ │ ┌────────────────▼────────────────────────────┐ │ Block Device Drivers (块设备驱动) │ │ - SCSI, NVMe, virtio-blk, ... │ └────────────────┬────────────────────────────┘ │ ┌────────────────▼────────────────────────────┐ │ Physical Storage (物理存储) │ │ - HDD, SSD, NVMe, RAID, ... │ └─────────────────────────────────────────────┘
|
核心数据结构概览
1. VFS 层核心结构
在 Linux 内核中,VFS 使用以下核心数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
struct inode { umode_t i_mode; unsigned short i_opflags; kuid_t i_uid; kgid_t i_gid; unsigned int i_flags;
const struct inode_operations *i_op; struct super_block *i_sb; struct address_space *i_mapping;
loff_t i_size; struct timespec64 i_atime; struct timespec64 i_mtime; struct timespec64 i_ctime;
unsigned long i_ino; dev_t i_rdev;
};
struct dentry { unsigned int d_flags; seqcount_spinlock_t d_seq; struct hlist_bl_node d_hash; struct dentry *d_parent; struct qstr d_name; struct inode *d_inode;
const struct dentry_operations *d_op; struct super_block *d_sb;
};
struct file { struct path f_path; struct inode *f_inode; const struct file_operations *f_op;
spinlock_t f_lock; atomic_long_t f_count; unsigned int f_flags; fmode_t f_mode; loff_t f_pos;
struct address_space *f_mapping;
};
struct super_block { struct list_head s_list; dev_t s_dev; unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes; struct file_system_type *s_type; const struct super_operations *s_op;
unsigned long s_flags; unsigned long s_magic; struct dentry *s_root;
};
|
2. Block Layer 核心结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
struct bio { struct bio *bi_next; struct block_device *bi_bdev; unsigned int bi_opf;
unsigned short bi_vcnt; unsigned short bi_max_vecs;
atomic_t __bi_cnt; struct bio_vec *bi_io_vec;
bio_end_io_t *bi_end_io; void *bi_private;
};
struct request { struct request_queue *q; struct blk_mq_ctx *mq_ctx; struct blk_mq_hw_ctx *mq_hctx;
unsigned int cmd_flags; enum rq_qos_id rq_qos_id;
sector_t __sector; unsigned int __data_len;
struct bio *bio; struct bio *biotail;
};
|
关键源码文件位置
基于 Linux 6.4-rc1 内核,以下是主要的源码文件位置:
VFS 层
fs/ - 文件系统核心代码
fs/namei.c - 路径查找和解析
fs/open.c - 文件打开操作
fs/read_write.c - 读写操作
fs/inode.c - inode 管理
fs/dcache.c - dentry 缓存
fs/super.c - 超级块管理
Block Layer
block/ - 块设备层代码
block/blk-core.c - 核心功能
block/blk-mq.c - 多队列实现
block/bio.c - bio 处理
block/blk-merge.c - 请求合并
block/blk-settings.c - 块设备设置
IO Schedulers
block/mq-deadline.c - Deadline 调度器
block/bfq-iosched.c - BFQ (Budget Fair Queueing) 调度器
block/kyber-iosched.c - Kyber 调度器
具体文件系统
fs/ext4/ - Ext4 文件系统
fs/btrfs/ - Btrfs 文件系统
fs/xfs/ - XFS 文件系统
Page Cache
mm/filemap.c - 文件映射和页缓存
mm/readahead.c - 预读机制
fs/buffer.c - 缓冲区缓存
一个文件读取的完整流程
让我们通过一个简单的 read() 系统调用,看看数据是如何从磁盘流向用户空间的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| 用户程序: read(fd, buffer, size) | v 系统调用入口: sys_read() (fs/read_write.c) | v VFS 层: vfs_read() | - 检查权限和文件状态 | - 调用 file->f_op->read_iter() v 文件系统层: ext4_file_read_iter() (以 Ext4 为例) | - 调用 generic_file_read_iter() v Page Cache: filemap_read() (mm/filemap.c) | - 在页缓存中查找数据 | - 如果缓存命中,直接返回 | - 如果缓存未命中,继续... v 文件系统 readpage: ext4_readpage() | - 准备从磁盘读取数据 | - 创建 bio 结构 v Block Layer: submit_bio() (block/bio.c) | - 将 bio 提交到块设备层 | - 可能进行请求合并 v IO Scheduler: 调度器处理请求 | - mq-deadline / BFQ / Kyber | - 优化 I/O 顺序 v Block Driver: SCSI/NVMe/... 驱动程序 | - 向硬件发送命令 v Hardware: 物理存储设备执行读取 | v (中断返回) Block Driver: 中断处理程序 | v Block Layer: bio_endio() | - 调用 bio->bi_end_io() 回调 v Page Cache: 将数据标记为最新 | v VFS: 将数据复制到用户空间缓冲区 | v 用户程序: read() 返回
|
核心概念解析
1. 页缓存(Page Cache)
页缓存是 Linux 内存管理的核心组件之一,它缓存文件内容在内存中,避免重复的磁盘访问。
关键特性:
- 以页(通常 4KB)为单位缓存文件数据
- 使用
struct address_space 管理文件到物理页的映射
- 支持预读(readahead)机制优化顺序读取
- 支持回写(writeback)机制延迟写入
代码位置: mm/filemap.c
2. 块 I/O 层(Block Layer)
块设备层是连接文件系统和设备驱动的中间层。
核心功能:
- 将文件系统的 I/O 请求转换为块设备请求
- I/O 请求的合并和重排序
- 支持多队列(blk-mq)架构
- 提供 I/O 统计和 QoS 控制
代码位置: block/
3. VFS(Virtual Filesystem Switch)
VFS 是所有文件系统的抽象层,提供统一的接口。
设计理念:
- 对上层提供统一的系统调用接口
- 对下层提供标准的文件系统操作接口
- 使用面向对象的设计思想(通过函数指针实现多态)
代码位置: fs/
代码示例:创建一个简单的文件系统
为了更好地理解 VFS,让我们看一个极简的文件系统示例(基于 ramfs):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| static const struct super_operations simple_super_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, };
static const struct inode_operations simple_dir_inode_operations = { .create = simple_create, .lookup = simple_lookup, .link = simple_link, .unlink = simple_unlink, .mkdir = simple_mkdir, .rmdir = simple_rmdir, .mknod = simple_mknod, .rename = simple_rename, };
static const struct file_operations simple_file_operations = { .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = noop_fsync, .llseek = generic_file_llseek, };
static int simple_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode;
sb->s_blocksize = PAGE_SIZE; sb->s_blocksize_bits = PAGE_SHIFT; sb->s_magic = 0x12345678; sb->s_op = &simple_super_ops;
inode = new_inode(sb); if (!inode) return -ENOMEM;
inode->i_ino = 1; inode->i_mode = S_IFDIR | 0755; inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_file_operations;
sb->s_root = d_make_root(inode); if (!sb->s_root) return -ENOMEM;
return 0; }
|
性能关键路径
在 Linux 存储栈中,以下是几个性能关键路径:
1. 快速路径(Fast Path)
- 页缓存命中
- 零拷贝操作(sendfile, splice)
- 直接 I/O(绕过页缓存)
2. 慢速路径(Slow Path)
- 页缓存未命中导致的磁盘 I/O
- 同步写入和 fsync 操作
- 元数据操作(创建、删除文件)
调试工具
在学习和调试存储系统时,以下工具非常有用:
系统工具
1 2 3 4 5 6 7 8 9 10 11 12
| iostat -x 1
blktrace -d /dev/sda -o - | blkparse -i -
cat /proc/meminfo | grep -i cache
cat /proc/sys/fs/dentry-state cat /proc/sys/fs/inode-state
|
内核工具
1 2 3 4 5 6
| trace-cmd record -e block:* -e vfs:* command
biolatency biosnoop
|
下一篇预告
在下一篇文章中,我们将深入探讨 VFS 虚拟文件系统层,包括:
- inode、dentry、file 的生命周期管理
- 路径查找算法的实现细节
- dcache(dentry cache)的设计与优化
- VFS 如何处理各种文件操作的源码分析
参考资料
- Linux 内核源码(v6.4-rc1):https://git.kernel.org/
- Linux 内核文档:
Documentation/filesystems/
- 《深入 Linux 内核架构》
- 《Linux 内核设计与实现》
作者注: 本系列所有源码分析基于 Linux 6.4-rc1 内核版本。随着内核的演进,部分实现细节可能会有所变化,但核心设计理念保持相对稳定。