虚拟文件系统(VFS)
VFS 是 Linux 内核中的一个抽象层,为用户空间程序提供统一的文件操作接口,屏蔽了底层不同文件系统的实现细节。
VFS 架构
┌─────────────────────────────────────────────────┐
│ 用户空间应用程序 │
│ open() read() write() close() stat() │
└────────────────┬────────────────────────────────┘
│
系统调用接口 (System Call Interface)
│
┌────────────────┼────────────────────────────────┐
│ ↓ │
│ ┌──────────────────────────┐ │
│ │ 虚拟文件系统 (VFS) │ │
│ │ ┌────────────────────┐ │ │
│ │ │ superblock │ │ │
│ │ │ inode │ │ │
│ │ │ dentry │ │ │
│ │ │ file │ │ │
│ │ └────────────────────┘ │ │
│ └──────────┬───────────────┘ │
│ │ │
│ ┌──────────┼──────────────────────┐ │
│ ↓ ↓ ↓ ↓ │
│ ┌────┐ ┌────┐ ┌─────┐ ┌──────┐ │
│ │Ext4│ │XFS │ │Btrfs│ │ NFS │ │
│ └────┘ └────┘ └─────┘ └──────┘ │
└─────────────────────────────────────────────────┘
VFS 核心数据结构
超级块(superblock)
描述文件系统的元数据:
// include/linux/fs.h
struct super_block {
struct list_head s_list; // 超级块链表
dev_t s_dev; // 设备标识符
unsigned long s_blocksize; // 块大小
loff_t s_maxbytes; // 最大文件大小
struct file_system_type *s_type; // 文件系统类型
const struct super_operations *s_op; // 超级块操作
struct dentry *s_root; // 根目录 dentry
struct list_head s_inodes; // inode 链表
struct list_head s_dirty; // 脏 inode 链表
// 文件系统特定数据
void *s_fs_info;
};
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*dirty_inode)(struct inode *, int flags);
int (*write_inode)(struct inode *, struct writeback_control *);
int (*drop_inode)(struct inode *);
void (*evict_inode)(struct inode *);
void (*put_super)(struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*statfs)(struct dentry *, struct kstatfs *);
};
inode(索引节点)
描述文件的元数据:
// include/linux/fs.h
struct inode {
// 文件类型和权限
umode_t i_mode; // 文件类型和权限
unsigned short i_opflags;
kuid_t i_uid; // 所有者 UID
kgid_t i_gid; // 所有者 GID
unsigned int i_flags;
// inode 操作
const struct inode_operations *i_op;
struct super_block *i_sb; // 所属超级块
// 文件大小和块
loff_t i_size; // 文件大小
struct timespec64 i_atime; // 访问时间
struct timespec64 i_mtime; // 修改时间
struct timespec64 i_ctime; // 状态改变时间
// 链接和引用计数
unsigned int i_nlink; // 硬链接数
dev_t i_rdev; // 设备号
// 文件操作
const struct file_operations *i_fop;
// 地址空间(页缓存)
struct address_space *i_mapping;
// 块设备
unsigned long i_ino; // inode 号
union {
unsigned long i_blocks; // 块数
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
// 文件系统特定数据
void *i_private;
};
struct inode_operations {
struct dentry *(*lookup)(struct inode *, struct dentry *, unsigned int);
int (*create)(struct inode *, struct dentry *, umode_t, bool);
int (*link)(struct dentry *, struct inode *, struct dentry *);
int (*unlink)(struct inode *, struct dentry *);
int (*symlink)(struct inode *, struct dentry *, const char *);
int (*mkdir)(struct inode *, struct dentry *, umode_t);
int (*rmdir)(struct inode *, struct dentry *);
int (*rename)(struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr)(struct dentry *, struct iattr *);
int (*getattr)(const struct path *, struct kstat *, u32, unsigned int);
};
dentry(目录项)
描述目录项(文件路径的组成部分):
// include/linux/dcache.h
struct dentry {
// 标志和状态
unsigned int d_flags;
seqcount_t d_seq;
// 父目录和子目录
struct dentry *d_parent; // 父目录
struct qstr d_name; // 目录项名称
// 关联的 inode
struct inode *d_inode; // 关联的 inode
// 目录项操作
const struct dentry_operations *d_op;
// 超级块
struct super_block *d_sb;
// 时间戳
unsigned long d_time;
// 哈希链表
struct hlist_bl_node d_hash;
// 子目录链表
struct list_head d_child;
struct list_head d_subdirs;
// LRU 链表
struct list_head d_lru;
union {
struct list_head d_child;
struct rcu_head d_rcu;
} d_u;
};
struct dentry_operations {
int (*d_revalidate)(struct dentry *, unsigned int);
int (*d_weak_revalidate)(struct dentry *, unsigned int);
int (*d_hash)(const struct dentry *, struct qstr *);
int (*d_compare)(const struct dentry *,
unsigned int, const char *, const struct qstr *);
int (*d_delete)(const struct dentry *);
int (*d_init)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
};
file(文件对象)
描述打开的文件:
// include/linux/fs.h
struct file {
// 文件路径
struct path f_path;
struct inode *f_inode; // 缓存的 inode
// 文件操作
const struct file_operations *f_op;
// 文件标志和模式
unsigned int f_flags; // 打开标志
fmode_t f_mode; // 访问模式
// 文件位置
loff_t f_pos; // 当前文件位置
// 引用计数
atomic_long_t f_count;
// 所有者
struct fown_struct f_owner;
const struct cred *f_cred;
// 地址空间
struct address_space *f_mapping;
// 私有数据
void *private_data;
};
struct file_operations {
struct module *owner;
loff_t (*llseek)(struct file *, loff_t, int);
ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter)(struct kiocb *, struct iov_iter *);
ssize_t (*write_iter)(struct kiocb *, struct iov_iter *);
int (*iterate)(struct file *, struct dir_context *);
int (*iterate_shared)(struct file *, struct dir_context *);
__poll_t (*poll)(struct file *, struct poll_table_struct *);
long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long);
long (*compat_ioctl)(struct file *, unsigned int, unsigned long);
int (*mmap)(struct file *, struct vm_area_struct *);
int (*open)(struct inode *, struct file *);
int (*flush)(struct file *, fl_owner_t id);
int (*release)(struct inode *, struct file *);
int (*fsync)(struct file *, loff_t, loff_t, int datasync);
int (*fasync)(int, struct file *, int);
int (*lock)(struct file *, int, struct file_lock *);
};
VFS 缓存机制
dentry cache
// 目录项缓存统计
// fs/dcache.c
// 查看 dcache 统计
cat /proc/sys/fs/dentry-state
# 输出:nr_dentry nr_unused age_limit want_pages
// 清理 dcache
echo 2 > /proc/sys/vm/drop_caches // 清理 dentry 和 inode
inode cache
// 查看 inode 缓存
cat /proc/sys/fs/inode-state
# 输出:nr_inodes nr_free_inodes preshrink
// slabtop 查看缓存
slabtop -o | grep -E 'dentry|inode'
page cache
// 查看页缓存统计
cat /proc/meminfo | grep -i cache
// 清理页缓存
sync
echo 1 > /proc/sys/vm/drop_caches // 仅清理 page cache
echo 3 > /proc/sys/vm/drop_caches // 清理所有缓存
文件路径解析
路径查找过程
// fs/namei.c
// 路径解析流程
1. path_openat()
├─ 初始化查找上下文
└─ 调用 link_path_walk()
2. link_path_walk()
├─ 逐个组件解析路径
├─ 查找 dentry cache
└─ 如果未命中,调用 i_op->lookup()
3. lookup_slow()
├─ 调用文件系统的 lookup 函数
├─ 读取磁盘数据
└─ 创建新的 dentry 和 inode
4. do_last()
└─ 处理最后一个路径组件
示例:打开 /home/user/test.txt
1. 从根目录 dentry 开始
2. 查找 "home" 目录项
- 检查 dcache
- 如果未命中,读取根目录的磁盘块
3. 查找 "user" 目录项
- 检查 dcache
- 如果未命中,读取 /home 的磁盘块
4. 查找 "test.txt" 文件
- 检查 dcache
- 如果未命中,读取 /home/user 的磁盘块
5. 创建 file 对象
6. 返回文件描述符
实践:查看 VFS 信息
查看文件系统类型
#!/bin/bash
# show-filesystems.sh
echo "=== 已注册的文件系统 ==="
cat /proc/filesystems
echo ""
echo "=== 已挂载的文件系统 ==="
mount | column -t
echo ""
echo "=== 各文件系统使用情况 ==="
df -hT
查看 inode 信息
# 查看文件的 inode 号
ls -i file.txt
# 查看 inode 详细信息
stat file.txt
# 查看文件系统 inode 使用情况
df -i
# 找到特定 inode 号的文件
find / -inum 12345
# 查看进程打开的文件
lsof -p <PID>
监控文件操作
# 使用 inotify 监控文件变化
inotifywait -m /path/to/watch
# 监控特定事件
inotifywait -m -e modify,create,delete /path/to/watch
# 使用 fanotify 监控文件系统
# 需要编写 C 程序使用 fanotify API
VFS 调试
// 示例:跟踪文件操作
// trace-file-ops.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
int fd;
struct stat st;
char buf[1024];
// open 系统调用
printf("Opening file...\n");
fd = open("/tmp/test.txt", O_RDWR | O_CREAT, 0644);
// fstat 系统调用
printf("Getting file stats...\n");
fstat(fd, &st);
printf("Inode: %ld, Size: %ld\n", st.st_ino, st.st_size);
// write 系统调用
printf("Writing to file...\n");
write(fd, "Hello VFS\n", 10);
// lseek 系统调用
printf("Seeking to beginning...\n");
lseek(fd, 0, SEEK_SET);
// read 系统调用
printf("Reading from file...\n");
read(fd, buf, sizeof(buf));
printf("Content: %s", buf);
// close 系统调用
printf("Closing file...\n");
close(fd);
return 0;
}
使用 strace 跟踪:
gcc -o trace trace-file-ops.c
strace -e trace=open,close,read,write,lseek,fstat ./trace
性能优化
减少系统调用
// 使用 mmap 代替 read/write
#include <sys/mman.h>
int fd = open("large_file.dat", O_RDONLY);
struct stat st;
fstat(fd, &st);
void *mapped = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
// 直接访问内存,无需 read() 系统调用
close(fd);
munmap(mapped, st.st_size);
批量操作
// 使用 readv/writev 批量读写
#include <sys/uio.h>
struct iovec iov[3];
iov[0].iov_base = buf1;
iov[0].iov_len = len1;
iov[1].iov_base = buf2;
iov[1].iov_len = len2;
iov[2].iov_base = buf3;
iov[2].iov_len = len3;
// 一次系统调用读取多个缓冲区
readv(fd, iov, 3);
异步 I/O
// 使用 io_uring (Linux 5.1+)
#include <liburing.h>
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, len, offset);
io_uring_submit(&ring);
struct io_uring_cqe *cqe;
io_uring_wait_cqe(&ring, &cqe);
// 处理完成事件
io_uring_cqe_seen(&ring, cqe);
下一步:学习 Ext4 文件系统详解 章节。