Linux内核文件系统入门–自己实现一个内存文件系统
1. 概述
大家好,今天我们要做点有趣的事情——实现一个简单的内存文件系统。在传统的磁盘文件系统、网络文件系统之外,内存文件系统凭借其速度快、无持久化等特点有着独特的应用场景。通过 VFS
的支持,创建一个内存文件系统变得相对容易。只需要完成三步:
- 注册文件系统类型:让内核知道我们有一个新的文件系统。
- 实现
inode
操作:定义文件系统中的对象如何响应各种操作(例如:创建/删除文件)。 - 实现文件操作:定义文件数据的具体读写方式。
接下来,咱们一步步实现。完整代码在这里 https://gitcode.com/weixin_47763623/myfs/overview
2. 注册文件系统类型
实现一个内存文件系统的第一步是将它注册为内核中的一个文件系统类型。文件系统的注册入口是 file_system_type
结构体。它提供了文件系统的基本信息和操作入口,使得内核可以识别并使用我们的文件系统。接下来,我们将一步步讲解如何定义和注册这个结构。
2.1 定义和注册 file_system_type
首先,我们定义一个名为 myfs
的文件系统类型,并注册它:
// 文件系统类型定义
static struct file_system_type myfs_type = {
.name = "myfs", // 文件系统名称
.mount = myfs_mount, // 挂载函数,定义如何在挂载点安装文件系统(需要自己实现)
.kill_sb = myfs_kill_super, // 卸载函数,定义如何释放文件系统资源(需要自己实现)
.owner = THIS_MODULE, // 指定模块所有者
};
// 初始化文件系统模块
static int __init myfs_init(void) {
int ret = register_filesystem(&myfs_type); // 注册文件系统(VFS 提供)
if (ret)
printk(KERN_ERR "myfs: unable to register myfs\n"); // 注册失败时打印错误信息
else
printk(KERN_INFO "myfs: myfs registered\n"); // 成功注册时打印信息
return ret;
}
// 退出文件系统模块
static void __exit myfs_exit(void) {
int ret = unregister_filesystem(&myfs_type); // 注销文件系统(VFS 提供)
if (ret)
printk(KERN_ERR "myfs: unable to unregister myfs\n");
}
解释
-
myfs_type
是一个file_system_type
结构体,描述了文件系统的名称、挂载和卸载行为。.name
用于指定文件系统的名字,在挂载文件系统时可以用它来识别。.mount
指向挂载函数myfs_mount
,定义文件系统如何被挂载。.kill_sb
指向卸载函数myfs_kill_super
,用于释放文件系统的资源。.owner
表示所属模块,用于内核模块的引用计数管理。
-
register_filesystem
是将文件系统注册到内核的关键函数(VFS 提供)。成功注册后,内核就能够识别和处理我们定义的myfs
文件系统。
2.2 填充超级块
文件系统的核心之一是 super_block
,它管理着文件系统的全局状态信息,并充当根目录的 inode
容器。实现文件系统时,我们需要提供一个函数来填充 super_block
的各个字段。下面是具体实现:
// 填充超级块信息
static int myfs_fill_super(struct super_block *sb, void *data, int silent) {
sb->s_magic = MYFS_MAGIC; // 设置文件系统的魔数,用于标识文件系统类型
sb->s_op = &myfs_super_ops; // 指定超级块的操作集合
sb->s_fs_info = NULL; // 额外的文件系统信息,可以为空
// 创建根目录的 inode
struct inode *root_inode = myfs_get_inode(sb, NULL, S_IFDIR | 0755, 0); // 需要自己实现
if (!root_inode)
return -ENOMEM; // 如果内存分配失败,返回错误
// 将根目录 inode 转换为 dentry 并设置为超级块的根
sb->s_root = d_make_root(root_inode);