linux内核 自定义文件系统
Linux VFS 是一层抽象层,所有真实文件系统(ext4/ext2/xfs/ 自定义 fs)都必须实现 VFS 规定的超级块、inode、dentry、file_operations等接口。
自定义文件系统本质:填充 VFS 接口 + 自己实现存储 / 逻辑。
第 1 步:定义文件系统名称与全局标识
所有文件系统必须向内核注册一个唯一类型名(如myfs)。
#include <linux/module.h> #include <linux/fs.h> #include <linux/pseudo_fs.h> #include <linux/magic.h> // 自定义文件系统魔数(唯一标识) #define MYFS_MAGIC 0x66666666 // 文件系统名称 #define MYFS_NAME "myfs"第 2 步:实现超级块操作(super_operations)
超级块(super_block)是文件系统的总控结构,管理整个文件系统。必须实现:挂载时初始化、删除 inode两个核心函数。
// 1. 释放 inode(必须实现) static void myfs_put_super(struct super_block *sb) { // 这里可释放自定义私有数据 pr_info("myfs: 超级块销毁\n"); } // 2. 定义超级块操作集合 static const struct super_operations myfs_sops = { .put_super = myfs_put_super, // 卸载时调用 .statfs = simple_statfs, // 简单文件系统使用内核默认实现 };第 3 步:实现 inode 与文件操作(file_operations)
- inode:管理文件元数据(权限、大小、类型)
- file_operations:文件读写 / 打开 / 关闭等用户态调用接口
我们实现一个最简单的内存文件系统(仅支持读固定内容):
// 自定义文件读操作 static ssize_t myfs_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { const char *msg = "Hello My Custom Filesystem!\n"; size_t msg_len = strlen(msg); // 偏移量超过内容长度,返回0 if (*ppos >= msg_len) return 0; // 复制数据到用户态 if (copy_to_user(buf, msg, msg_len)) return -EFAULT; *ppos = msg_len; return msg_len; } // 定义文件操作集合 static const struct file_operations myfs_file_ops = { .read = myfs_read, // 实现读 };第 4 步:实现根目录创建(dentry)
文件系统挂载后必须有根目录/,需要手动创建根 inode 和目录项。
// 分配并初始化 inode static struct inode *myfs_make_inode(struct super_block *sb, umode_t mode) { struct inode *inode = new_inode(sb); if (!inode) return NULL; // 初始化 inode 元数据 inode->i_ino = get_next_ino(); // 自动分配 inode 号 inode->i_mode = mode; inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); return inode; } // 创建根目录 static int myfs_fill_super(struct super_block *sb, struct fs_context *fc) { struct inode *root_inode; struct dentry *root_dentry; // 1. 初始化超级块 sb->s_magic = MYFS_MAGIC; sb->s_op = &myfs_sops; // 绑定超级块操作 // 2. 创建根 inode(目录类型) root_inode = myfs_make_inode(sb, S_IFDIR | 0755); if (!root_inode) return -ENOMEM; // 根目录使用内核通用目录操作 root_inode->i_op = &simple_dir_inode_operations; root_inode->i_fop = &simple_dir_operations; // 3. 创建根 dentry root_dentry = d_make_root(root_inode); if (!root_dentry) return -ENOMEM; sb->s_root = root_dentry; pr_info("myfs: 超级块初始化成功\n"); return 0; }第 5 步:实现文件系统挂载接口
内核通过fs_context完成挂载,必须实现挂载回调函数。
// 挂载时的初始化函数 static int myfs_get_tree(struct fs_context *fc) { return get_tree_single(fc, myfs_fill_super); } // 文件系统上下文操作 static const struct fs_context_operations myfs_context_ops = { .get_tree = myfs_get_tree, }; // 初始化上下文 static int myfs_init_fs_context(struct fs_context *fc) { fc->ops = &myfs_context_ops; return 0; } // 定义文件系统类型(最核心结构) static struct file_system_type myfs_type = { .name = MYFS_NAME, // 挂载名称 .init_fs_context = myfs_init_fs_context, .kill_sb = kill_litter_super, // 内核默认释放超级块 .owner = THIS_MODULE, };第 6 步:模块注册与卸载(入口 / 出口)
// 模块加载:注册文件系统 static int __init myfs_init(void) { int ret = register_filesystem(&myfs_type); if (ret) { pr_err("myfs: 注册失败\n"); return ret; } pr_info("myfs: 自定义文件系统注册成功\n"); return 0; } // 模块卸载:注销文件系统 static void __exit myfs_exit(void) { unregister_filesystem(&myfs_type); pr_info("myfs: 自定义文件系统注销成功\n"); } module_init(myfs_init); module_exit(myfs_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Linux Custom Filesystem Demo"); MODULE_AUTHOR("My Name");第 7 步:编写 Makefile 编译内核模块
obj-m += myfs.o # 请替换为你的内核源码路径 / 本机内核头文件 KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean编译、挂载、测试全步骤
1. 编译模块
make2. 加载内核模块
sudo insmod myfs.ko查看是否注册成功:
cat /proc/filesystems | grep myfs # 输出:myfs 表示成功3. 创建挂载点并挂载
sudo mkdir -p /mnt/myfs sudo mount -t myfs none /mnt/myfs4. 测试自定义文件系统
# 查看挂载 df -h /mnt/myfs # 读文件(我们内置的固定字符串) cat /mnt/myfs/anyfile # 输出:Hello My Custom Filesystem!5. 卸载
sudo umount /mnt/myfs sudo rmmod myfs自定义文件系统完整功能扩展
上面是最小可用版本,真实文件系统需要扩展以下接口:
| 结构 | 必须实现的功能 | 用途 |
|---|---|---|
super_operations | alloc_inode / destroy_inode | 管理 inode 生命周期 |
inode_operations | create / lookup / mkdir / unlink | 创建文件、查找、目录操作 |
file_operations | read / write / llseek / open | 文件读写、偏移 |
address_space | readpage / writepage | 分页读写(持久化存储) |
典型扩展方向
- 持久化存储:将数据写入磁盘 / 分区(替代内存存储)
- 目录文件管理:支持创建、删除、重命名文件
- 权限管理:UID/GID、权限位校验
- 日志功能:类似 ext4 日志,保证崩溃安全
- 缓存优化:使用页缓存提升性能
关键内核结构关系
用户态:open/read/write ↓ VFS 虚拟文件系统 ↓ file_operations → inode_operations → super_operations ↓ ↓ ↓ 自定义文件系统实现(你的代码)- super_block:整个文件系统的管理者
- inode:单个文件的元数据
- dentry:目录项,连接文件名与 inode
- file:进程打开的文件实例
核心操作集
struct file_system_type
文件系统类型本身的操作(注册 / 挂载 / 销毁)
作用:定义文件系统名字、挂载方式、销毁超级块方法。必须实现,是文件系统的入口。
关键函数:
init_fs_context:初始化挂载上下文kill_sb:卸载时销毁超级块mount(旧接口)
struct super_operations
超级块操作集(文件系统全局管理)
作用:管理整个文件系统的生命周期、inode 分配、统计信息。
必须 / 常用函数:
alloc_inode:创建新 inodedestroy_inode:销毁 inodeput_super:卸载文件系统statfs:获取磁盘统计信息(df 命令)sync_fs:同步数据到磁盘
struct inode_operations
inode 元数据操作集(文件 / 目录属性管理)
作用:文件创建、删除、重命名、权限、链接管理。文件系统核心中的核心。
关键函数:
create:创建文件lookup:查找文件名对应 inodemkdir:创建目录unlink:删除文件rmdir:删除目录rename:重命名文件setattr:修改权限 / 大小 / 时间getattr:获取属性(stat 命令)
struct file_operations
文件数据操作集(用户态直接调用)
作用:进程打开、读写、执行文件时的实际逻辑。用户态open/read/write最终落到这里。
关键函数:
open:打开文件read:读文件write:写文件llseek:文件偏移mmap:内存映射fsync:同步文件数据release:关闭文件
struct dentry_operations
目录项操作集(文件名 <-> inode 映射)
作用:管理目录缓存、比较文件名、释放目录项。
常用函数:
d_compare:比较文件名d_delete:删除目录项d_release:释放目录项内存
struct address_space_operations
页缓存操作集(文件数据缓存 / 持久化)
作用:内核页缓存与磁盘 / 存储之间的读写。实现文件持久化必须用这个。
关键函数:
readpage:读磁盘到缓存writepage:写缓存到磁盘write_begin/write_end:写入流程
struct fs_context_operations
文件系统挂载上下文操作集(新内核 4.18+)
作用:解析挂载参数、创建超级块。现代文件系统必须实现。挂载上下文操作集,真正实现 mount 逻辑;
函数:
get_tree:创建并填充超级块parse_param:解析挂载参数
属于struct fs_context:
struct fs_context { const struct fs_context_operations *ops; // ← 在这里! ... };struct vm_operations_struct
虚拟内存操作集(mmap 扩展)
作用:文件内存映射时的页面异常处理。用于自定义 mmap 行为。
struct simple_link_operations
快速符号链接操作
专门用于优化符号链接,避免分配独立 page。
struct block_device_operations
块设备打开 / 释放 / IOCTL 操作,块设备文件系统使用。
最简单内存文件系统(如上面 demo)
必须实现:
file_system_typesuper_operationsfs_context_operationsinode_operations(使用内核通用版)file_operations(自定义读写)
支持目录 / 文件创建删除的完整文件系统
必须额外实现:
inode_operations全套(create/mkdir/unlink/rename)dentry_operations
支持磁盘持久化的真实文件系统
必须额外实现:
address_space_operations(readpage/writepage)
操作集关系图
文件系统挂载 ↓ file_system_type → fs_context → super_operations(全局) ↓ inode_operations(元数据) ←← 超级块 ↓ file_operations(数据) dentry_operations(目录) ↓ address_space_operations(页缓存/磁盘)内核 mount 完整调用流程
内核层调用链:
- sys_mount → 进入 VFS
- 分配
fs_context - 调用
file_system_type->init_fs_context - 解析参数:
fc->ops->parse_param - 核心:
fc->ops->get_tree - get_tree 调用
get_tree_single/get_tree_bdev等 helper - 回调你的
fill_super初始化 sb、根 inode、根 dentry - 将超级块挂入 VFS 目录树
- 完成 mount
fill_super 不是操作集成员,而是用来填充超级块的回调函数,由 VFS 辅助函数调用,服务于 mount 过程。
mount → fs_context → get_tree → fill_super完整结构图:
struct fs_context_operations ← 这是【挂载操作集】 ↓ .get_tree → 调用 get_tree_single/fc, 【myfs_fill_super】 ↓ 你的 fill_super 函数用户执行 mount ↓ VFS 创建 fs_context ↓ fc->ops = fs_context_operations ← 你定义的 ↓ 调用 fc->ops->get_tree() ↓ get_tree 调用 fill_super总结
这就是Linux 内核自定义文件系统的完整标准流程,核心是:
- 基于 VFS 框架,填充 4 套核心操作接口
- 实现超级块初始化 + 根目录创建
- 注册文件系统、挂载、测试
- 进阶扩展持久化、目录、权限、缓存
file_system_type—— 文件系统本身super_operations—— 全局管理inode_operations—— 文件 / 目录元数据(增删改查)file_operations—— 文件数据(读写)dentry_operations—— 目录项address_space_operations—— 页缓存 / 持久化fs_context_operations—— 挂载vm_operations_struct—— 内存映射
