前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >揭开虚拟文件系统的云雾(1)(基于linux1.2.13)

揭开虚拟文件系统的云雾(1)(基于linux1.2.13)

作者头像
theanarkh
发布2020-01-02 11:54:47
4850
发布2020-01-02 11:54:47
举报
文章被收录于专栏:原创分享

这一篇我们来看看,虚拟文件系统是如何抹平各个文件系统的差异,又是如何和具体的文件系统串起来的。 我们先来回顾一下之前的讲的内容。

代码语言:javascript
复制
    void mount_root(void){
        ...

    for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
        if(retval)
            break;
        // 没有关联到设备则不需要往下执行,有些文件系统是没有对应的底层设备的
        if (!fs_type->requires_dev)
            continue;
        // 读根设备的超级块,设备的第一扇区是分区表,接着是超级块
        sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
        // 读取成功
        if (sb) {
            // 根节点 
            inode = sb->s_mounted;
            inode->i_count += 3 ;   /* NOTE! it is logically used 4 times, not 1 */
            sb->s_covered = inode;
            sb->s_flags = root_mountflags;
            // 当前进程(init进程)的根目录和工作目录设置为根节点
            current->fs->pwd = inode;
            current->fs->root = inode;
            printk ("VFS: Mounted root (%s filesystem)%s.\n",
                fs_type->name,
                (sb->s_flags & MS_RDONLY) ? " readonly" : "");
            // 直接返回,即第一个读取成功的文件系统成为根文件系统
            return;
        }
    }
        ...
    }

我们看到由read_super加载超级块的内容,假设根文件系统是ext文件系统。从函数sys_setup中我们知道ext文件系统的read_super函数是ext_read_super。

代码语言:javascript
复制
#ifdef CONFIG_EXT_FS
    register_filesystem(&(struct file_system_type)
        {ext_read_super, "ext", 1, NULL});
#endif

接着往下看。

代码语言:javascript
复制
struct super_block *ext_read_super(struct super_block *s,void *data, 
                   int silent)
{
    struct buffer_head *bh;
    struct ext_super_block *es;
    int dev = s->s_dev,block;

    lock_super(s);
    set_blocksize(dev, BLOCK_SIZE);
    // 读取设备的内容,即超级块的内容
    if (!(bh = bread(dev, 1, BLOCK_SIZE))) {
        s->s_dev=0;
        unlock_super(s);
        printk("EXT-fs: unable to read superblock\n");
        return NULL;
    }
    // 文件系统的一些属性
    es = (struct ext_super_block *) bh->b_data;
    // 超级块的一些属性
    ...
    s->s_dev = dev;
    // 操作函数集
    s->s_op = &ext_sops;
    // 读取根节点,超级块的s_mounted字段指向根节点
    if (!(s->s_mounted = iget(s,EXT_ROOT_INO))) {
        s->s_dev=0;
        printk("EXT-fs: get root inode failed\n");
        return NULL;
    }
    return s;
}

我们看到这个函数主要做的几件事情是 1 从硬盘读取超级块的内容 2 设置超级块的操作函数集 3 最后读取文件系统的根节点。 读超级块内容这个没有太多需要解释的,我们先看看超级块操作函数集的定义

代码语言:javascript
复制
static struct super_operations ext_sops = { 
    ext_read_inode,
    NULL,
    ext_write_inode,
    ext_put_inode,
    ext_put_super,
    ext_write_super,
    ext_statfs,
    NULL
};

然后我们看一下读取inode的函数iget。

代码语言:javascript
复制
extern inline struct inode * iget(struct super_block * sb,int nr)
{
    return __iget(sb,nr,1);
}

__iget函数的主要逻辑是获取一个新的inode结构体,然后执行read_inode(inode)从硬盘把inode数据读取进来(inode.c后续单独分析)。核心代码如下。

代码语言:javascript
复制
if (!empty) {
        h->updating++;
        // 获取一个空闲inode
        empty = get_empty_inode();
        if (!--h->updating)
            wake_up(&update_wait);
        if (empty)
            goto repeat;
        return (NULL);
    }
    inode = empty;
    inode->i_sb = sb;
    inode->i_dev = sb->s_dev;
    inode->i_ino = nr;
    inode->i_flags = sb->s_flags;
    put_last_free(inode);
    insert_inode_hash(inode);
    read_inode(inode);

下面直接看read_inode函数。

代码语言:javascript
复制
static void read_inode(struct inode * inode)
{
    lock_inode(inode);
    if (inode->i_sb && inode->i_sb->s_op && inode->i_sb->s_op->read_inode)
        inode->i_sb->s_op->read_inode(inode);
    unlock_inode(inode);
}

我们看文章的开头可以发现,read_inode函数在ext文件系统中的实现函数是ext_read_inode。

代码语言:javascript
复制
void ext_read_inode(struct inode * inode)
{
    struct buffer_head * bh;
    struct ext_inode * raw_inode;
    int block;
    // 每个硬盘块可以存储的inode结构体数,+2是代表启动扇区、超级块数据的硬盘块
    block = 2 + (inode->i_ino-1)/EXT_INODES_PER_BLOCK;
    // 读取某设备的某个块
    if (!(bh=bread(inode->i_dev, block, BLOCK_SIZE)))
        panic("unable to read i-node block");
    // 取余算出偏移从而得到inode结构体的数据
    raw_inode = ((struct ext_inode *) bh->b_data) +
        (inode->i_ino-1)%EXT_INODES_PER_BLOCK;
    // 复制某些字段到内存的inode结构体
    inode->i_mode = raw_inode->i_mode;
    inode->i_uid = raw_inode->i_uid;
    inode->i_gid = raw_inode->i_gid;
    inode->i_nlink = raw_inode->i_nlinks;
    inode->i_size = raw_inode->i_size;
    inode->i_mtime = inode->i_atime = inode->i_ctime = raw_inode->i_time;
    inode->i_blocks = inode->i_blksize = 0;
    // 字符和块设备的i_zone[0]是设备号
    if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
        inode->i_rdev = raw_inode->i_zone[0];
    else for (block = 0; block < 12; block++)
        // 否则i_zone里存的是文件数据的硬盘直接块号或者间接块号 
        inode->u.ext_i.i_data[block] = raw_inode->i_zone[block];
    brelse(bh);
    inode->i_op = NULL;
    // 不同类型的文件赋值不同的操作函数集
    if (S_ISREG(inode->i_mode))
        inode->i_op = &ext_file_inode_operations;
    else if (S_ISDIR(inode->i_mode))
        inode->i_op = &ext_dir_inode_operations;
    else if (S_ISLNK(inode->i_mode))
        inode->i_op = &ext_symlink_inode_operations;
    else if (S_ISCHR(inode->i_mode))
        inode->i_op = &chrdev_inode_operations;
    else if (S_ISBLK(inode->i_mode))
        inode->i_op = &blkdev_inode_operations;
    else if (S_ISFIFO(inode->i_mode))
        init_fifo(inode);
}

至此,把根文件系统的超级块和根节点inode都读取进来了。再回头看mount_root函数的代码

代码语言:javascript
复制
    inode = sb->s_mounted;
    // 当前进程(init进程)的根目录和工作目录设置为根节点
    current->fs->pwd = inode;
    current->fs->root = inode;

下面我们看看此时的架构图。

这一篇就先说到这里,现在有根节点了,下一篇看一下如何进行文件的读写和多文件系统是如何一起工作的。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档