注册模块和mount

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;
}

好!完成

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容