获取lfs文件_本地文件系统小计(三): super_block

本文详细介绍了Linux文件系统中的super_block结构,阐述了其在内存中的创建与管理,以及与各文件系统super_block的关系。特别讨论了在ceph文件系统中的实现,并解析了如何通过super_block获取根目录的dentry和inode,揭示了根目录inode号为2的来源。
摘要由CSDN通过智能技术生成

上一篇提到问题:根目录inode号为2是怎么获取到的?

在回答这个问题时,需要了解下super_block

super block:记录文件系统的整体信息,包括inode/block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等。

在mount时,vfs层根据实际文件系统存放在块设备上的管理信息(也可以叫做文件系统的元数据),在内存中建立一个vfs层的struct super_block,并且加入vfs的全局链表super_blocks的尾部,super_blocks的定义和初始化处在fs/super.c里面

static LIST_HEAD(super_blocks);

以cephfs为例,来介绍这个过程,(ceph_mount --> sget )首先是ceph_mount(fs/ceph/super.c)

static struct dentry *ceph_mount(struct file_system_type *fs_type,
		       int flags, const char *dev_name, void *data)
{
	struct super_block *sb;
	struct ceph_fs_client *fsc;
	struct dentry *res;
	int err;
	int (*compare_super)(struct super_block *, void *) = ceph_compare_super;
	struct ceph_mount_options *fsopt = NULL;
	struct ceph_options *opt = NULL;

	dout("ceph_mountn");
        ......
	fsc = create_fs_client(fsopt, opt);
        ......
	sb = sget(fs_type, compare_super, ceph_set_super, flags, fsc); //重点函数
        ...... 
}

在sget里面会建立一个super_block,并加入全局链表super_blocks尾部

struct super_block *sget(struct file_system_type *type,
			int (*test)(struct super_block *,void *),
			int (*set)(struct super_block *,void *),
			int flags,
			void *data)
{
	struct user_namespace *user_ns = current_user_ns();
	struct super_block *s = NULL;
	struct super_block *old;
	int err;
        ......
retry:
        ......
	if (!s) {
		spin_unlock(&sb_lock);
                //分配一个super_block
		s = alloc_super(type, (flags & ~SB_SUBMOUNT), user_ns);

	}
	s->s_type = type;
	strlcpy(s->s_id, type->name, sizeof(s->s_id));
	list_add_tail(&s->s_list, &super_blocks);
        ......
	return s;
}

vfs层的super_block只存在于内存中,它在文件系统mount时建立,在文件系统umount时删除。这里得分清楚vfs层的super_block和各实际文件系统的superblock,对于每个具体的文件系统来说,都有各自的super_block(如ext4文件系统的super_block结构是struct ext4_super_block),它们是存于磁盘中的。

vfs中的super_block和各个实际的文件系统的super_block的联系是vfs层的super_block的void* s_fs_info 成员指向具体文件系统的信息(ext4中的struct ext4_sb_info)。这个过程是ext4_mount --> mount_bdev --> ext4_fill_super,首先是ext4_mount(fs/ext4/super.c)

static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
		       const char *dev_name, void *data)
{
	return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
}

然后是mount_bdev(fs/super.c)

struct dentry *mount_bdev(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	struct block_device *bdev;
	struct super_block *s;
	fmode_t mode = FMODE_READ | FMODE_EXCL;
	int error = 0;

        ......
	s = sget(fs_type, test_bdev_super, set_bdev_super, flags | SB_NOSEC,
		 bdev);
        ......
	if (s->s_root) {
                ......
	} else {
		s->s_mode = mode;
		snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);

		error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
                ......
	}
        ......
}

接下来是ext4_fill_super(fs/ext4/super.c)

static int ext4_fill_super(struct super_block *sb, void *data, int silent)
{
	struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
	char *orig_data = kstrdup(data, GFP_KERNEL);
	struct buffer_head *bh;
	struct ext4_super_block *es = NULL;
	struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
	......
	sb->s_fs_info = sbi;
	sbi->s_sb = sb;
        ......

	if (!(bh = sb_bread_unmovable(sb, logical_sb_block))) {
		ext4_msg(sb, KERN_ERR, "unable to read superblock");
		goto out_fail;
	}
	/*
	 * Note: s_es must be initialized as soon as possible because
	 *       some ext4 macro-instructions depend on its value
	 */
	es = (struct ext4_super_block *) (bh->b_data + offset);
	sbi->s_es = es;
        ......
}

struct ext4_sb_info中也保存了struct super_block和ext4_super_block指针,画个图表示下他们的关系。

e03b216cec26ec30862ebc99541184d2.png
vfs层的super_block和ext4的super_block的联系

这里特别介绍下ceph文件系统:

ceph文件系统中没有类似超级块(struct ext4_super_block)的结构,但是有类似(struct ext4_sb_info)的结构体struct ceph_fs_client,这是因为文件系统的元数据信息是由mds保存在元数据池里面。vfs层的super_block的void* s_fs_info 成员指向的是cephfs中的struct ceph_fs_client(ceph_mount -> sget -> ceph_set _super)。首先是ceph_mount(fs/ceph/super.c),代码参考上面,然后通过sget走到 ceph_set _super(fs/ceph/super.c)

static int ceph_set_super(struct super_block *s, void *data)
{
	struct ceph_fs_client *fsc = data;
	int ret;

	dout("set_super %p data %pn", s, data);

	s->s_flags = fsc->mount_options->sb_flags;
	s->s_maxbytes = MAX_LFS_FILESIZE;

	s->s_xattr = ceph_xattr_handlers;
	s->s_fs_info = fsc;
	fsc->sb = s;
	fsc->max_file_size = 1ULL << 40; /* temp value until we get mdsmap */

	s->s_op = &ceph_super_ops;
	s->s_d_op = &ceph_dentry_ops;
	s->s_export_op = &ceph_export_ops;

	s->s_time_gran = 1;

	ret = set_anon_super(s, NULL);  /* what is that second arg for? */
	if (ret != 0)
		goto fail;

	return ret;

fail:
	s->s_fs_info = NULL;
	fsc->sb = NULL;
	return ret;
}

ceph_fs_client中也保存了super_block的指针,画图表示,如下图

05784751196acfad31e870666869a33a.png
vfs层的super_block和cephfs的ceph_fs_client的联系

填充缓存中的vfs层的super_block时,直接发请求从mds获取,比如要获取根目录的dentry,通过open_root_dentry函数发送ceph_mds_op_getattr请求,从请求回复中获得struct inode(vfs),然后再获得dentry。

接下来解释下:根目录inode号为2是怎么获取到的?

从上面的内容,我们可以知道,在mount文件系统时,会在内存中建立一个super_block;而在struct super_block中有struct dentry *s_root成员,s_root就指向的是根目录的dentry,而在struct dentry中有struct inode *d_inode成员,保存的是根目录的inode,inode号就可以从inode中获取。在上一章中,其实不需要再去读索引区中inode为2的inode内容,因为这部分内容已经在缓存中了,只需要去根目录具体所指的block区读取目录项即可。

这里又提到了一个新的概念:dentry(目录项)。什么是dentry,这将在下一章讲到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值