Preview
https://juejin.cn/post/7033574024198406174 ,一定要好好看看这篇文章中的各个数据结构,不然看代码你会很懵
前置知识
- 我们自己写的文件系统肯定不能和内核一起编译,不是技术上不可行而是太麻烦,不灵活,如果都这样干那内核就太大了,所以我们可以动态的将我们的模块加载到内核中,使用insmod命令就可以了,使用rmmod删除模块,所以我们要编写加载模块时需要调用的代码
- 文件系统是通过mount命令将其真正的加载到内核中的,原理可以参考:https://zhuanlan.zhihu.com/p/144893220 ,mount是个很有意思的命令,我觉得大家都需要熟悉他的一些特点,所以我们要编写mount时需要调用的代码
源码解析
代码在super.c中
首先这个文件的入口如下,module_init和module_exit是用来注册\删除模块的,把自己写的注册\删除时需要调用的函数的指针传给他(HUST_fs_init/HUST_fs_exit),那么调用insmod命令时就会调用对应的函数
// 驱动加载退出模块
module_init(HUST_fs_init);
module_exit(HUST_fs_exit);
MODULE_LICENSE("MIT");
MODULE_AUTHOR("cv");
这里重点说注册时调用的函数 HUST_fs_init, 在这个函数中回去调用register_filesystem这个系统函数,register_filesystem是用来注册文件系统的,接收一个file_system_type类型的实例对象指针,file_system_type是内核中定义的,在preview中的那篇文章有详细介绍,之后类似的内核数据结构不再强调
int HUST_fs_init(void)
{
...
ret = register_filesystem(&HUST_fs_type);
...
}
HUST_fs_type中的数据如下, 主要是设置了文件系统的名字,和mount、umount时调用的函数
解释下,实例化结构体时在变量名前加.是为了指定只显示初始化这几个变量
struct file_system_type HUST_fs_type = {
.owner = THIS_MODULE,
.name = "HUST_fs",
.mount = HUST_fs_mount,
.kill_sb = HUST_fs_kill_superblock, /* unmount */
};
重点讲下mount时调用的函数HUST_fs_mount,主要调用的是mount_bdev这个内核函数,接收一个回调函数HUST_fs_fill_super,下面主要讲讲HUST_fs_fill_super这个函数
struct dentry *HUST_fs_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
{
...
/* This is a generic mount function that accepts a callback. */
ret = mount_bdev(fs_type, flags, dev_name, data, HUST_fs_fill_super);
...
}
HUST_fs_fill_super如下
int HUST_fs_fill_super(struct super_block *sb, void *data, int silent)
{
int ret = -EPERM;
// 将sb放到缓存中
struct buffer_head *bh;
// sb_bread 就是用来读取磁盘中超级块的数据的,我对其理解不深...
// 反正下面这个写法就是标准写法,超级块的指针存放在bh的b_data中
bh = sb_bread(sb, 1);
BUG_ON(!bh);
struct HUST_fs_super_block *sb_disk;
// 将b_data转成HUST_fs_super_block类型的指针
// 顺便提一句,数据类型其实是用来指明数据长度的,但是指针的长度只跟机器的位数有关(因为指针存放的是地址,地址的位数是固定的),也就是说对于特定的机器,其指针长度固定
// 那么指针为啥需要类型呢, 原因如下:
// 1、指针类型不是用来指示该指针变量的长度,而是指明该指针所存地址中存放的数据的类型(有点绕,待我想到更好表达再改),所以指针类型是为了取值的时候知道数据的长度
// 2、指针的偏移:都知道指针是可以进行加一减一来指向下一个元素的(数组的实现原理),所以需要知道每个元素的长度
// sb_disk就是我们格式化磁盘时存放在磁盘中的超级块数据
sb_disk = (struct HUST_fs_super_block *)bh->b_data;
...
// 这里的magic就是一个校验位,如果magic和我们规定的MAGIC_NUM不一样,证明文件系统损坏了
// 一般来说可以强制跳过这个校验,进行只读挂载
if (sb_disk->magic != MAGIC_NUM) {
printk(KERN_ERR "Magic number not match!\n");
goto release;
}
// 数据校验...
if (sb_disk->block_size != 4096) {
printk(KERN_ERR "HUST_fs expects a blocksize of %d\n", 4096);
ret = -EFAULT;
goto release;
}
//fill vfs super block
// 搞清楚哈,sb是vfs的超级块,sb_disk使我们磁盘中的超级块,具体去看开头的文章
sb->s_magic = sb_disk->magic;
sb->s_fs_info = sb_disk;
sb->s_maxbytes = HUST_BLOCKSIZE * HUST_N_BLOCKS; /* Max file size */
// 这里就是注册超级块能做的操作,其实就是将对应的函数赋值给他
sb->s_op = &HUST_fs_super_ops;
//-----------test get inode-----
// 测试获取inode,raw_root_node就是我们格式化时存放在磁盘中的root节点的信息,后面会用
struct HUST_inode raw_root_node;
if (HUST_fs_get_inode(sb, HUST_ROOT_INODE_NUM, &raw_root_node) != -1) {
printk(KERN_INFO "Get inode sucessfully!\n");
printk(KERN_INFO "root blocks is %llu and block[0] is %llu\n",
raw_root_node.blocks, raw_root_node.block[0]);
}
//-----------end test-----------
struct inode *root_inode;
// 这是个系统调用,new一个vfs的inode
root_inode = new_inode(sb);
if (!root_inode)
return -ENOMEM;
/* Our root inode. It doesn't contain useful information for now.
* Note that i_ino must not be 0, since valid inode numbers start at
* 1. */
inode_init_owner(root_inode, NULL, S_IFDIR |
S_IRUSR | S_IXUSR |
S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
// 给root_inode填充数据
root_inode->i_sb = sb;
root_inode->i_ino = HUST_ROOT_INODE_NUM;
root_inode->i_atime = root_inode->i_mtime = root_inode->i_ctime =
current_time(root_inode);
root_inode->i_mode = raw_root_node.mode;
root_inode->i_size = raw_root_node.dir_children_count;
//root_inode->i_private = HUST_fs_get_inode(sb, HUST_ROOT_INODE_NUM);
/* Doesn't really matter. Since this is a directory, it "should"
* have a link count of 2. See btrfs, though, where directories
* always have a link count of 1. That appears to work, even though
* it created a number of bug reports in other tools. :-) Just
* search the web for that topic. */
inc_nlink(root_inode);
/* What can you do with our inode? */
// 这是注册inode能做的操作
root_inode->i_op = &HUST_fs_inode_ops;
root_inode->i_fop = &HUST_fs_dir_ops;
/* Make a struct dentry from our inode and store it in our
* superblock. */
sb->s_root = d_make_root(root_inode);
if (!sb->s_root)
return -ENOMEM;
release:
brelse(bh);
return 0;
}
好!完成