上一篇提到问题:根目录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指针,画个图表示下他们的关系。
这里特别介绍下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的指针,画图表示,如下图
填充缓存中的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,这将在下一章讲到。