本文通过分析内核挂载和初始化源码剖析内核是如何知道调用的是具体哪个文件系统的函数。
一:文件系统注册结构体函数(参考:https://books.google.co.jp/books?id=h0lltXyJ8aIC&pg=PT498&lpg=PT498&dq=struct+file_system_type&source=bl&ots=gP5rKV8fKT&sig=ACfU3U0GOHI4xtDWlFbLBKh6G8swkzA-ig&hl=en&sa=X&ved=2ahUKEwjd_rnI85fkAhXpxYsBHVneC6k4ChDoATAGegQICBAB#v=onepage&q=struct%20file_system_type&f=false)
先来看两个函数
在系统初始化时,register_filesystem() 函数在编译内核时被调用给每个指定的文件系统;该函数插入file_system_type对象到filesystem-type列表中。
该函数也可以在一个模块实现一个文件系统被加载时调用(意思大概就是我们可以手动加载一个模块,该模块是实现一个文件系统时可以被调用)。这种情况下文件系统也可以在卸载模块是被取消注册(通过调用unregister_filesystem函数)。
二、挂载流程
1. 关于mount参数详解可以参考这里:https://linux.die.net/man/8/mount
重点: 如果没有指定-t选项,或者是制定了auto类型,mount将尝试去猜测期望的类型。Mount使用blkid或者volume_id库去猜测文件系统类型。如果这样还是没找到相似的,mount将尝试去读取文件/etc/filesystems,或者如果该文件不存在,就读取/proc/filesystems文件。在文件里面列出的所有的文件系统类型将会被尝试,除了那些被打有“nodev"标签的(比如 devpts,proc和nfs),可能是灾难性的结果,因此如果你的数据是有价值的,还是别让mount去猜测了。
攒满假设讨论制定了-t ext4 的挂载,那么该类型字符串会从用户空间拷贝到内核空间中往下传递。
2. ksys_mount
->do_mount
->do_new_mount
->get_fs_type
->fs_context_for_mount
->do_new_mount_fc
->vfs_create_mount(fc)
->do_add_mount
(1) 函数get_fs_type接收一个文件系统名称作为它的参数,扫描已经注册的文件系统列表,查看字段并且返回对应于file_system_type对象的指针。
// 在file_systems链表中查找是否能找到一个已经注册的文件系统类型
//找到的话返回该file_system_type
static struct file_system_type *__get_fs_type(const char *name, int len)
{
struct file_system_type *fs;
read_lock(&file_systems_lock);
fs = *(find_filesystem(name, len));
if (fs && !try_module_get(fs->owner))
fs = NULL;
read_unlock(&file_systems_lock);
return fs;
}
(2)重点函数fs_context_for_mount
fc = fs_context_for_mount(type, sb_flags);
(1)vfs_create_mount函数主要是调用alloc_vfsmnt 分配一个新的挂载的文件系统描述符,且做一些初始化。
struct vfsmount *vfs_create_mount(struct fs_context *fc)
{
struct mount *mnt;
if (!fc->root)
return ERR_PTR(-EINVAL);
mnt = alloc_vfsmnt(fc->source ?: "none");//分配一个struct mount结构
if (!mnt)
return ERR_PTR(-ENOMEM);
if (fc->sb_flags & SB_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
//初始化 mnt中的struct vfsmount结构
atomic_inc(&fc->root->d_sb->s_active);
mnt->mnt.mnt_sb = fc->root->d_sb; // 超级块结构
mnt->mnt.mnt_root = dget(fc->root);
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
lock_mount_hash();
list_add_tail(&mnt->mnt_instance, &mnt->mnt.mnt_sb->s_mounts);
unlock_mount_hash();
return &mnt->mnt;
}
struct vfsmount {
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
} __randomize_layout;
(2).do_add_mount 将新的挂载文件系统对象插如到namespace list中
(三)
其实有一个问题,我一直很好奇到底对于具体的某个文件系统内核怎么知道在挂载的时候要去调用ext4对应的函数,虽然你在mount的时候传递了ext4 类型进来。 就比如说怎么知道是调用的ext4_fill_super 而不是其它类型的文件系统。
就是我们知道比如对于ext4文件系统回在系统加载内核启动的时候注册file_system_type 是属于ext4的。
在fs/ext4/super.c文件最下面注册了ext4模块
MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others");
MODULE_DESCRIPTION("Fourth Extended Filesystem");
MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: crc32c");
module_init(ext4_init_fs)
module_exit(ext4_exit_fs)
函数ext4_init_fs里调用了err = register_filesystem(&ext4_fs_type);
static struct file_system_type ext4_fs_type = {
.owner = THIS_MODULE,
.name = "ext4",
.mount = ext4_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext4");
重点来了,就是这里挂载的时候传入了ext4_file_super
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);
}
在ext4_file_super函数后面会指定操作函数
sbi->s_stripe = ext4_get_stripe_size(sbi);
sbi->s_extent_max_zeroout_kb = 32;
/*
* set up enough so that it can read an inode
*/
sb->s_op = &ext4_sops;
sb->s_export_op = &ext4_export_ops;
sb->s_xattr = ext4_xattr_handlers;
static const struct super_operations ext4_sops = {
.alloc_inode = ext4_alloc_inode,
.free_inode = ext4_free_in_core_inode,
.destroy_inode = ext4_destroy_inode,
.write_inode = ext4_write_inode,
.dirty_inode = ext4_dirty_inode,
.drop_inode = ext4_drop_inode,
.evict_inode = ext4_evict_inode,
.put_super = ext4_put_super,
.sync_fs = ext4_sync_fs,
.freeze_fs = ext4_freeze,
.unfreeze_fs = ext4_unfreeze,
.statfs = ext4_statfs,
.remount_fs = ext4_remount,
.show_options = ext4_show_options,
#ifdef CONFIG_QUOTA
.quota_read = ext4_quota_read,
.quota_write = ext4_quota_write,
.get_dquots = ext4_get_dquots,
#endif
.bdev_try_to_free_page = bdev_try_to_free_page,
};
更加详细的内容不做分析。