当前位置: 首页 > news >正文

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. 编译模块

make

2. 加载内核模块

sudo insmod myfs.ko

查看是否注册成功:

cat /proc/filesystems | grep myfs # 输出:myfs 表示成功

3. 创建挂载点并挂载

sudo mkdir -p /mnt/myfs sudo mount -t myfs none /mnt/myfs

4. 测试自定义文件系统

# 查看挂载 df -h /mnt/myfs # 读文件(我们内置的固定字符串) cat /mnt/myfs/anyfile # 输出:Hello My Custom Filesystem!

5. 卸载

sudo umount /mnt/myfs sudo rmmod myfs

自定义文件系统完整功能扩展

上面是最小可用版本,真实文件系统需要扩展以下接口:

结构必须实现的功能用途
super_operationsalloc_inode / destroy_inode管理 inode 生命周期
inode_operationscreate / lookup / mkdir / unlink创建文件、查找、目录操作
file_operationsread / write / llseek / open文件读写、偏移
address_spacereadpage / writepage分页读写(持久化存储)

典型扩展方向

  1. 持久化存储:将数据写入磁盘 / 分区(替代内存存储)
  2. 目录文件管理:支持创建、删除、重命名文件
  3. 权限管理:UID/GID、权限位校验
  4. 日志功能:类似 ext4 日志,保证崩溃安全
  5. 缓存优化:使用页缓存提升性能

关键内核结构关系

用户态: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:创建新 inode
  • destroy_inode:销毁 inode
  • put_super:卸载文件系统
  • statfs:获取磁盘统计信息(df 命令)
  • sync_fs:同步数据到磁盘

struct inode_operations

inode 元数据操作集(文件 / 目录属性管理)

作用:文件创建、删除、重命名、权限、链接管理。文件系统核心中的核心

关键函数:

  • create:创建文件
  • lookup:查找文件名对应 inode
  • mkdir:创建目录
  • 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)

必须实现:

  1. file_system_type
  2. super_operations
  3. fs_context_operations
  4. inode_operations(使用内核通用版)
  5. 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 完整调用流程

内核层调用链:

  1. sys_mount → 进入 VFS
  2. 分配fs_context
  3. 调用file_system_type->init_fs_context
  4. 解析参数:fc->ops->parse_param
  5. 核心:fc->ops->get_tree
  6. get_tree 调用get_tree_single/get_tree_bdev等 helper
  7. 回调你的fill_super初始化 sb、根 inode、根 dentry
  8. 将超级块挂入 VFS 目录树
  9. 完成 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—— 内存映射
http://www.cnnetsun.cn/news/1376015.html

相关文章:

  • 谷歌研究团队重磅发现:推理竟然是大模型“回忆“知识的秘密钥匙
  • Win10/Win11下用AHK一键切换显示器输入源(支持多品牌显示器)
  • 网关冗余协议选型指南:从金融到制造业的5个真实场景解析HSRP/VRRP选择
  • UABEAvalonia:跨平台Unity资源包管理工具的技术解析与应用指南
  • 探索大气压等离子体电离波在介质管中的动态传输机制
  • 从此告别拖延 10个AI论文工具测评:开源免费+毕业论文写作全攻略
  • UC3842芯片在反激电源中的应用:从电流环配置到电压反馈调试
  • DS18B20单总线温度传感器驱动与STM32F4实现
  • LM Studio + Anything LLM 本地知识库搭建全流程:从模型下载到API调用
  • 【uView】u-list-item 自定义样式实战:跨平台适配与动态样式注入
  • COMSOL相场法模拟水力压裂的六个案例及参考文献
  • LangChain工具实战:5个最常用工具组合提升你的AI开发效率(附代码)
  • 如何快速掌握MTKClient:联发科设备刷机与调试终极指南
  • Vue项目集成高德地图AMapUI组件库:从轨迹巡航到自定义标记的实战指南
  • MCP接入OAuth 2026究竟值不值得升级?2024Q3真实压测数据告诉你答案
  • 为什么你的MCP 2.0升级后仍被攻破?——解析密钥协商绕过、元数据泄露、可信通道降级这3个沉默杀手
  • LVDS实战:IBUFDS原语在FPGA高速接口中的关键配置与陷阱规避
  • 别再一股脑传Base64图片了!用JS精准提取富文本纯文本,翻译接口性能提升80%
  • Memcached 教程
  • Mirage Flow与新一代目标检测器:YOLOv11集成应用展望
  • 基于PyQt5的智能车调试上位机:从零搭建与协议解析实战
  • Kettle8.3之Windows与Linux双平台安装指南
  • 算法学习心得
  • StructBERT中文语义匹配系统实战:跨境电商商品描述语义对齐
  • kill-doc文档下载工具终极指南:告别繁琐下载,一键获取免费文档
  • FlowState Lab数据处理管道搭建:从原始数据到模型输入的完整流程
  • STM32F405RGT6飞控实战:从零搭建四轴飞行器的硬件选型与避坑指南
  • Z-Image-Turbo-辉夜巫女结合YOLOv8:实现生成图像的自动目标检测与标注
  • HY-MT1.5-7B翻译模型新手入门:零基础部署与多语言翻译测试
  • LTspice仿真实战:OP07反相放大器电路设计与精度验证