1.核心数据结构关系
c
用户空间 内核空间
│ │
▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ fd │───▶│ file │──▶│ inode │──▶│ sb │
│(int) │ │(struct) │ │(struct) │ │(struct) │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │
│ f_op i_op s_op
│ (file_ops) (inode_ops) (super_ops)
│ │ │ │
▼ ▼ ▼ ▼
进程FD表 VFS层 具体文件系统 块设备层
2.各核心概念详解
2.1.文件描述符(File Descriptor)
本质:进程级整数索引,指向内核的 struct file
c
进程 task_struct
└── files_struct
└── fd_array[NR_OPEN_DEFAULT] // 默认 64 项
├── fd[0] → struct file* // stdin
├── fd[1] → struct file* // stdout
├── fd[2] → struct file* // stderr
└── fd[3] → struct file* // 打开的文件
关键特性:
- 进程私有:每个进程独立维护 fd 表
- 继承性:fork() 时复制,exec() 时保留(除非 O_CLOEXEC)
- 最小分配:总是返回最小可用整数
2.2.文件对象(struct file)
本质:打开文件的动态视图,代表一次打开操作
c
struct file {
struct path f_path; // 文件路径 (dentry + vfsmount)
struct inode *f_inode; // 指向 inode
const struct file_operations *f_op; // 操作方法集
loff_t f_pos; // 当前读写位置
fmode_t f_mode; // 打开模式 (O_RDONLY, O_RDWR...)
struct fown_struct f_owner; // 用于信号驱动 I/O
unsigned int f_flags; // 打开标志 (O_NONBLOCK, O_SYNC...)
struct list_head f_ep_links; // epoll 关联
struct address_space *f_mapping; // 页缓存映射
// ...
};
重要区分:
| 概念 | 生命周期 | 数量关系 |
|---|---|---|
struct file |
打开到关闭 | N 个进程可共享同一 file(dup/fork) |
struct inode |
文件存在期间 | 1 个 inode 可被多个 file 引用 |
2.3.索引节点(struct inode)
本质:文件的静态元数据,唯一标识一个文件
c
struct inode {
umode_t i_mode; // 文件类型 + 权限
uid_t i_uid; // 属主
gid_t i_gid; // 属组
loff_t i_size; // 文件大小
struct timespec64 i_atime; // 访问时间
struct timespec64 i_mtime; // 修改时间
struct timespec64 i_ctime; // 变更时间
struct super_block *i_sb; // 所属超级块
const struct inode_operations *i_op; // inode 操作
struct address_space *i_mapping; // 页缓存地址空间
// 文件系统私有数据
union {
struct ext4_inode_info ext4_i;
struct xfs_inode xfs_i;
// ...
};
};
关键机制:
- inode cache:内核缓存最近使用的 inode,避免重复读取磁盘
- 引用计数:i_count(内存引用)和 i_nlink(硬链接数)
- i_hash:全局哈希表,加速查找
3.虚拟文件系统(VFS)
本质:抽象层,统一所有文件系统的接口
c
┌─────────────────────────────────────┐
│ 系统调用层 │
│ (open/read/write/close/mmap...) │
└─────────────┬───────────────────────┘
▼
┌─────────────────────────────────────┐
│ VFS 层 │
│ ┌─────────┐ ┌─────────┐ ┌──────┐ │
│ │ file ops│ │inode ops│ │sb ops│ │
│ │generic_ │ │generic_ │ │ │ │
│ │read() │ │create() │ │ │ │
│ └─────────┘ └─────────┘ └──────┘ │
└─────────────┬───────────────────────┘
▼
┌─────────────────────────────────────┐
│ 具体文件系统层 │
│ ext4 │ xfs │ btrfs │ nfs │ fuse │
└─────────────┬───────────────────────┘
▼
┌─────────────────────────────────────┐
│ 块设备/网络层 │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │SCSI │ │NVMe │ │NFS │ │
│ └─────┘ └─────┘ └─────┘ │
└─────────────────────────────────────┘
VFS 核心对象:
| 对象 | 作用 | 关键操作 |
|---|---|---|
super_block |
文件系统实例 | s_op: mount/umount/statfs |
inode |
文件元数据 | i_op: create/link/unlink |
dentry |
目录项缓存 | d_op: d_hash/d_compare |
file |
打开文件 | f_op: read/write/mmap/poll |
address_space |
页缓存管理 | a_ops: readpage/writepage |
4.性能优化
4.1.页缓存(Page Cache)优化
c
// 核心结构
struct address_space {
struct inode *host; // 所属 inode
struct radix_tree_root page_tree; // 页缓存 radix 树
spinlock_t tree_lock;
const struct address_space_operations *a_ops;
// readpage/writepage/write_begin/write_end
};
优化手段:
| 技术 | 原理 | 适用场景 |
|---|---|---|
| 预读(Readahead) | 顺序读取时提前加载后续页 | 大文件顺序读 |
| 延迟写(Writeback) | 脏页不立即刷盘,批量回写 | 写密集型 |
| mmap + MAP_POPULATE | 预建立页表映射 | 启动时加载 |
| posix_fadvise | 提示访问模式 | 数据库、日志 |
c
# 查看页缓存命中率
cat /proc/vmstat | grep -E "pgpgin|pgpgout|pswpin|pswpout"
# 调整脏页回写阈值
sysctl vm.dirty_ratio=40 # 脏页占总内存比例
sysctl vm.dirty_expire_centisecs=3000 # 脏页过期时
4.2.目录项缓存(Dentry Cache / DCACHE)
作用:缓存路径解析结果,避免重复遍历目录
c
# 查看 dentry cache 状态
cat /proc/slabinfo | grep dentry
# 手动清理(谨慎使用)
echo 2 > /proc/sys/vm/drop_caches # 清理 dentry 和 inode
优化:
- 减少不必要的 stat() 调用
- 使用 openat() 相对路径,复用已解析的 dentry
- 避免深层目录结构
4.3.inode 缓存优化
c
# 查看 inode 缓存
cat /proc/slabinfo | grep inode
# 文件系统挂载选项
mount -o noatime /dev/sda1 /data # 禁用访问时间更新
mount -o relatime /dev/sda1 /data # 相对时间更新(推荐)
4.4.I/O 调度与异步 I/O
| 技术 | 说明 |
|---|---|
| io_uring | 现代异步 I/O,零拷贝、批处理,性能最佳 |
| AIO | 传统 POSIX AIO,有诸多限制 |
| epoll + O_NONBLOCK | 网络/文件事件驱动 |
| Direct I/O | 绕过页缓存,O_DIRECT,数据库常用 |
c
// io_uring 示例(简化)
struct io_uring ring;
io_uring_queue_init(32, &ring, 0);
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buf, size, offset);
io_uring_submit(&ring);
struct io_uring_cqe *cqe;
io_uring_wait_cqe(&ring, &cqe);
// 处理完成
io_uring_cqe_seen(&ring, cqe);
4.5.文件描述符优化
c
# 查看进程 fd 限制
ulimit -n
# 系统级限制
cat /proc/sys/fs/file-max # 系统最大打开文件数
cat /proc/sys/fs/nr_open # 单个进程最大 fd
# 高并发场景调整
sysctl fs.file-max=2097152
4.6.零拷贝技术
c
传统方式:磁盘 → 页缓存 → 用户缓冲区 → socket 缓冲区 → 网卡
↑______内核空间______↑ ↑____用户空间____↑ ↑__内核__↑
4 次拷贝,4 次上下文切换
sendfile:磁盘 → 页缓存 ────────────────→ socket 缓冲区 → 网卡
2 次拷贝(DMA 引擎直接搬运),2 次上下文切换
splice: 管道直接连接两个 fd,完全在内核空间完成
实例:
c
// sendfile 零拷贝
sendfile(out_fd, in_fd, &offset, count);
// splice 零拷贝
int pipefd[2];
pipe(pipefd);
splice(in_fd, &off_in, pipefd[1], NULL, len, SPLICE_F_MOVE);
splice(pipefd[0], NULL, out_fd, &off_out, len, SPLICE_F_MOVE);
4.7.调优检查清单
| 层面 | 检查项 | 命令/配置 |
|---|---|---|
| 内存 | 页缓存命中率 | vmstat 1,关注 bi/bo |
| CPU | 软中断消耗 | cat /proc/softirqs |
| 磁盘 | I/O 调度器 | cat /sys/block/sda/queue/scheduler |
| 文件系统 | 挂载选项 | noatime, nodiratime |
| 应用 | fd 泄漏 | `ls /proc/ |
| 网络 | 零拷贝启用 | ethtool -k eth0 查看 tx-checksumming |