Linux操作系统根文件系统的注册与挂载

本文介绍了Linux内核中根文件系统(rootfs)的注册与挂载过程,包括mnt_init函数的作用、init_rootfs函数如何注册rootfs、rootfs挂载函数rootfs_mount的实现细节以及init_mount_tree函数如何构建挂载树。

    内核源码:linux-2.6.38.8.tar.bz2

    目标平台:ARM体系结构

 

    在Linux内核中,根文件系统的注册和挂载是在系统启动过程中所调用的mnt_init函数中实现的。源代码如下所示:

/* arch/arm/include/asm/cache.h */
#define __read_mostly __attribute__((__section__(".data..read_mostly")))  //存放在可执行文件的.data..read_mostly节

/* fs/namespace.c */
static struct list_head *mount_hashtable __read_mostly;
static struct kmem_cache *mnt_cache __read_mostly;
static struct rw_semaphore namespace_sem;

DEFINE_BRLOCK(vfsmount_lock); //定义vfsmount_lock锁相关函数

#define HASH_SHIFT ilog2(PAGE_SIZE / sizeof(struct list_head)) //PAGE_SIZE就是一页内存的大小(4096个字节)
#define HASH_SIZE (1UL << HASH_SHIFT) //HASH_SHIFT等于9,所以HASH_SIZE等于512

void __init mnt_init(void)
{
	unsigned u;
	int err;

	init_rwsem(&namespace_sem); //初始化读写信号量
	
	//创建mnt_cache缓存,用于struct vfsmount结构体实例
	mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct vfsmount),
			0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
			
	//为链式哈希表分配一页内存,用于存储该哈希表所有的链表表头
	mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);

	if (!mount_hashtable)
		panic("Failed to allocate mount hash table\n");

	printk("Mount-cache hash table entries: %lu\n", HASH_SIZE);
	
	for (u = 0; u < HASH_SIZE; u++)
		INIT_LIST_HEAD(&mount_hashtable[u]); //初始化所有链表表头,也是用于struct vfsmount实例

	br_lock_init(vfsmount_lock); //初始化vfsmount_lock锁

	err = sysfs_init(); //注册及挂载sysfs文件系统
	if (err)
		printk(KERN_WARNING "%s: sysfs_init error: %d\n",
			__func__, err);
	fs_kobj = kobject_create_and_add("fs", NULL); //在sysfs文件系统根目录下创建一个名叫fs的目录
	if (!fs_kobj)
		printk(KERN_WARNING "%s: kobj create error\n", __func__);
		
	init_rootfs(); //注册rootfs文件系统
	init_mount_tree(); //按函数名字面含义是初始化挂载树,也就是将rootfs文件系统作为整个系统的根文件系统
}

    1、init_rootfs函数主要用于注册根文件系统rootfs,源代码如下所示:

/* fs/ramfs/inode.c */
int __init init_rootfs(void)
{
	int err;

	err = bdi_init(&ramfs_backing_dev_info); //初始化后备存储介质相关信息
	if (err)
		return err;

	err = register_filesystem(&rootfs_fs_type); //注册文件系统
	if (err)
		bdi_destroy(&ramfs_backing_dev_info);

	return err;
}
    1.1、注册文件系统所涉及的数据和函数如下所示:
/* include/linux/fs.h */
struct file_system_type {
	const char *name; //文件系统名称,用以区分文件系统的类型
	int fs_flags; //标志,其中FS_REQUIRES_DEV表示该文件系统需要存储介质
	int (*get_sb) (struct file_system_type *, int,
		       const char *, void *, struct vfsmount *); //构建超级块
	struct dentry *(*mount) (struct file_system_type *, int,
		       const char *, void *); //挂载函数,与get_sb函数功能类似,只须定义其一即可
	void (*kill_sb) (struct super_block *); //销毁超级块
	struct module *owner; //以模块形式加载时有效
	struct file_system_type * next; //用于构建文件系统链表
	struct list_head fs_supers; //同名文件系统可能存在多个实例,它们的超级块都插入到该链表中

	//超级块相关锁检测模块
	struct lock_class_key s_lock_key;
	struct lock_class_key s_umount_key;
	struct lock_class_key s_vfs_rename_key;
	
	//i节点相关锁检测模块
	struct lock_class_key i_lock_key;
	struct lock_class_key i_mutex_key;
	struct lock_class_key i_mutex_dir_key;
	struct lock_class_key i_alloc_sem_key;
};

/* fs/filesystems.c */
static struct file_system_type *file_systems; //全局变量,所以它的初始值为空指针
static DEFINE_RWLOCK(file_systems_lock); //定义并初始化file_systems_lock锁

static struct file_system_type **find_filesystem(const char *name, unsigned len)
{
	struct file_system_type **p;
	
	for (p=&file_systems; *p; p=&(*p)->next) //遍历链表file_systems
		if (strlen((*p)->name) == len &&
		    strncmp((*p)->name, name, len) == 0) //文件系统名称相同则退出循环
			break;
	return p; //返回file_systems的地址或者某个file_system_type实例成员next的地址
}

int register_filesystem(struct file_system_type * fs)
{
	int res = 0;
	struct file_system_type ** p;

	BUG_ON(strchr(fs->name, '.')); //文件系统名称中含有.字符
	if (fs->next) //它的值应该为空指针
		return -EBUSY;
	INIT_LIST_HEAD(&fs->fs_supers); //初始化fs_supers成员
	write_lock(&file_systems_lock);
	p = find_filesystem(fs->name, strlen(fs->name));
	if (*p)
		res = -EBUSY; //相同名称的文件系统已经存在
	else
		*p = fs; //将新的文件系统插入到链表file_systems的末尾
	write_unlock(&file_systems_lock);
	return res;
}
    1.2、根文件系统rootfs所对应的struct  file_system_type结构体实例如下所示:
/* fs/ramfs/inode.c */
static struct file_system_type rootfs_fs_type = {
	.name		= "rootfs",
	.mount		= rootfs_mount,
	.kill_sb	= kill_litter_super,
};
    其中,文件系统标志fs_flags初始化为零,表示该文件系统没有后备存储介质,是直接存储在内存中的。

    (1)、rootfs_mount是特定于rootfs文件系统的挂载函数。源代码如下所示:

/* fs/ramfs/inode.c */
static struct dentry *rootfs_mount(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
	return mount_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super); //MS_NOUSER表示不能导出到用户层
}

/* fs/super.c */
struct dentry *mount_nodev(struct file_system_type *fs_type,
	int flags, void *data,
	int (*fill_super)(struct super_block *, void *, int))
{
	int error;
	struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); //查找或构建超级块

	if (IS_ERR(s))
		return ERR_CAST(s);

	s->s_flags = flags; //保存挂载标志

	error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); //初始化超级块相关成员
	if (error) {
		deactivate_locked_super(s);
		return ERR_PTR(error);
	}
	s->s_flags |= MS_ACTIVE; //激活文件系统
	return dget(s->s_root); //递增根目录的引用计数s->s_root->d_count
}
    sget函数从相同类型文件系统的链表中查找相同的超级块实例,如果找到则返回这个超级块,否则创建一个超级块实例。源代码如下所示:
/* fs/super.c */
LIST_HEAD(super_blocks); //系统中所有超级块的链表
DEFINE_SPINLOCK(sb_lock);

struct super_block *sget(struct file_system_type *type,
			int (*test)(struct super_block *,void *),
			int (*set)(struct super_block *,void *),
			void *data)
{
	struct super_block *s = NULL;
	struct super_block *old;
	int err;

retry:
	spin_lock(&sb_lock);
	if (test) { //test函数指针为真
		list_for_each_entry(old, &type->fs_supers, s_instances) { //遍历该类文件系统所有的超级块
			if (!test(old, data)) //返回真表示old超级块里的某个数据(不同的文件系统选取不同的数据)与data一致
				continue;
			if (!grab_super(old)) //old->s_active为零则跳转到retry
				goto retry;
			if (s) {
				up_write(&s->s_umount);
				destroy_super(s); //释放超级块相关内存
				s = NULL;
			}
			down_write(&old->s_umount);
			//未配置MS_BORN标志并且old->s_active递减后为零则调用old->kill_sb所指函数
			if (unlikely(!(old->s_flags & MS_BORN))) { 
				deactivate_locked_super(old);
				goto retry;
			}
			return old; //返回原有的超级块并且old->s_umount是加锁状态
		}
	}
	if (!s) { //s为空指针
		spin_unlock(&sb_lock);
		s = alloc_super(type); //分配新的超级块
		if (!s)
			return ERR_PTR(-ENOMEM); //分配失败则错误返回
		goto retry;  //这时data尚未保存到新的超级块中
	}
	
	//set函数必须定义,对于rootfs文件系统该函数指针指向set_anon_super函数
	//以零为主设备号和从struct ida unnamed_dev_ida中获取的次设备号合成设备ID来初始化s->s_dev成员
	//成员s->s_bdi更改为指向noop_backing_dev_info表示的存储介质
	err = set(s, data); 
	if (err) { //失败则销毁超级块并错误返回
		spin_unlock(&sb_lock);
		up_write(&s->s_umount);
		destroy_super(s);
		return ERR_PTR(err);
	}
	s->s_type = type; //指向所属的文件系统类型
	strlcpy(s->s_id, type->name, sizeof(s->s_id)); //拷贝文件系统名称
	list_add_tail(&s->s_list, &super_blocks); //插入超级块总链表
	list_add(&s->s_instances, &type->fs_supers); //插入所属的文件系统
	spin_unlock(&sb_lock);
	get_filesystem(type); //以模块加载并且该模块能够卸载时,该函数才不为空
	return s;
}
    其中,alloc_super函数用于分配存储超级块实例的内存并初始化一些成员。源代码如下所示:
/* fs/super.c */
static struct super_block *alloc_super(struct file_system_type *type)
{
	struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER); //分配存储超级块实例的内存并清零
	static const struct super_operations default_op; //其中的函数指针全部为空指针

	if (s) { //内存分配成功
		if (security_sb_alloc(s)) { //安全模块检查
			kfree(s);
			s = NULL;
			goto out;
		}
		//初始化s_files成员,对于SMP系统则动态分配per-cpu变量
#ifdef CONFIG_SMP
		s->s_files = alloc_percpu(struct list_head);
		if (!s->s_files) {
			security_sb_free(s); //安全模块检查
			kfree(s);
			s = NULL;
			goto out;
		} else { //分配成功
			int i;

			for_each_possible_cpu(i) //每个CPU都有该变量
				INIT_LIST_HEAD(per_cpu_ptr(s->s_files, i));
		}
#else
		INIT_LIST_HEAD(&s->s_files);
#endif
		s->s_bdi = &default_backing_dev_info;
		INIT_LIST_HEAD(&s->s_instances);
		INIT_HLIST_BL_HEAD(&s->s_anon);
		INIT_LIST_HEAD(&s->s_inodes);
		INIT_LIST_HEAD(&s->s_dentry_lru);
		init_rwsem(&s->s_umount);
		mutex_init(&s->s_lock);
		lockdep_set_class(&s->s_umount, &type->s_umount_key); //初始化该类死锁检测模块
		lockdep_set_class(&s->s_lock, &type->s_lock_key);
		down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING); //加锁s->s_umount
		s->s_count = 1;
		atomic_set(&s->s_active, 1);
		mutex_init(&s->s_vfs_rename_mutex);
		lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key);
		mutex_init(&s->s_dquot.dqio_mutex);
		mutex_init(&s->s_dquot.dqonoff_mutex);
		init_rwsem(&s->s_dquot.dqptr_sem);
		init_waitqueue_head(&s->s_wait_unfrozen);
		s->s_maxbytes = MAX_NON_LFS;
		s->s_op = &default_op;
		s->s_time_gran = 1000000000;
	}
out:
	return s;
}
    在这里,fill_super函数指针指向ramfs_fill_super函数,用于初始化超级块的一些成员,源代码如下所示:
/* fs/ramfs/inode.c */
int ramfs_fill_super(struct super_block *sb, void *data, int silent)
{
	struct ramfs_fs_info *fsi;
	struct inode *inode = NULL;
	struct dentry *root;
	int err;
	
	//将data保存到sb->s_options
	save_mount_options(sb, data);

	fsi = kzalloc(sizeof(struct ramfs_fs_info), GFP_KERNEL);
	sb->s_fs_info = fsi; //文件系统特有数据
	if (!fsi) {
		err = -ENOMEM;
		goto fail;
	}
	
	//对于rootfs文件系统,无data数据,仅将fsi->mount_opts.mode初始化为RAMFS_DEFAULT_MODE(0755)
	err = ramfs_parse_options(data, &fsi->mount_opts); 
	if (err)
		goto fail;

	sb->s_maxbytes		= MAX_LFS_FILESIZE;
	sb->s_blocksize		= PAGE_CACHE_SIZE;
	sb->s_blocksize_bits	= PAGE_CACHE_SHIFT;
	sb->s_magic		= RAMFS_MAGIC;
	sb->s_op		= &ramfs_ops; //超级块操作函数
	sb->s_time_gran		= 1;
	
	//生成根目录的i节点
	inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
	if (!inode) {
		err = -ENOMEM;
		goto fail;
	}
	
	//生成该文件系统的根目录项并与i节点关联
	root = d_alloc_root(inode);
	sb->s_root = root;
	if (!root) {
		err = -ENOMEM;
		goto fail;
	}

	return 0;
fail:
	kfree(fsi);
	sb->s_fs_info = NULL;
	iput(inode);
	return err;
}
    其中,由于rootfs根文件系统是基于ramfs文件系统实现的,所以使用ramfs_get_inode函数来生成一个i节点并使用d_alloc_root函数生成将之作为rootfs文件系统的根目录。源代码如下所示:
/* fs/ramfs/inode.c */
struct inode *ramfs_get_inode(struct super_block *sb,
				const struct inode *dir, int mode, dev_t dev)
{
	struct inode * inode = new_inode(sb); //分配并初始化i节点

	if (inode) {
		inode->i_ino = get_next_ino(); //获取i节点号
		inode_init_owner(inode, dir, mode); //初始化i节点的i_uid、i_gid和i_mode等三个成员
		//下面四行语句都是初始化成员i_mapping
		inode->i_mapping->a_ops = &ramfs_aops;
		inode->i_mapping->backing_dev_info = &ramfs_backing_dev_info;
		mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
		mapping_set_unevictable(inode->i_mapping);
		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; //文件时间初始化为当前时间
		switch (mode & S_IFMT) {
		default: //特殊文件
			init_special_inode(inode, mode, dev);
			break;
		case S_IFREG: //普通文件
			inode->i_op = &ramfs_file_inode_operations;
			inode->i_fop = &ramfs_file_operations;
			break;
		case S_IFDIR: //目录
			inode->i_op = &ramfs_dir_inode_operations;
			inode->i_fop = &simple_dir_operations;

			inc_nlink(inode); //目录i_nlink的初始值为2
			break;
		case S_IFLNK: //符号链接文件
			inode->i_op = &page_symlink_inode_operations;
			break;
		}
	}
	return inode;
}
/* fs/dcache.c */
struct dentry * d_alloc_root(struct inode * root_inode)
{
	struct dentry *res = NULL;

	if (root_inode) {
		static const struct qstr name = { .name = "/", .len = 1 }; //根目录名

		res = d_alloc(NULL, &name); //分配并初始化根目录项(因为第一个参数为NULL)
		if (res) {
			res->d_sb = root_inode->i_sb; //指向所属的超级块
			d_set_d_op(res, res->d_sb->s_d_op); //设置目录项操作函数及其标志
			res->d_parent = res; //根目录项的父目录项是其自身
			d_instantiate(res, root_inode); //关联目录项和i节点
		}
	}
	return res;
}
    每一个文件系统实例都有一个自身的根目录。

    (2)、kill_litter_super函数用于销毁超级块,源代码如下所示:

/* fs/super.c */
void kill_litter_super(struct super_block *sb)
{
	if (sb->s_root)
		d_genocide(sb->s_root); //为所有的目录项设置DCACHE_GENOCIDE标志
	kill_anon_super(sb);
}

void kill_anon_super(struct super_block *sb)
{
	int slot = MINOR(sb->s_dev);

	generic_shutdown_super(sb);
	spin_lock(&unnamed_dev_lock);
	ida_remove(&unnamed_dev_ida, slot); //释放次设备号
	if (slot < unnamed_dev_start)
		unnamed_dev_start = slot;
	spin_unlock(&unnamed_dev_lock);
}

void generic_shutdown_super(struct super_block *sb)
{
	const struct super_operations *sop = sb->s_op;

	if (sb->s_root) {
		shrink_dcache_for_umount(sb); //销毁sb->s_root和sb->s_anon链表上所有的目录项及其i节点
		sync_filesystem(sb); //刷新文件系统
		get_fs_excl(); //递增当前进程的fs_excl引用计数
		sb->s_flags &= ~MS_ACTIVE; //清除MS_ACTIVE标志

		fsnotify_unmount_inodes(&sb->s_inodes); //实现文件系统事件监控的IN_UNMOUNT事件

		evict_inodes(sb); //确保sb->s_inodes链表上没有引用计数为零的i节点

		if (sop->put_super)
			sop->put_super(sb); //调用特定于文件系统的释放函数

		if (!list_empty(&sb->s_inodes)) { //所属的i节点链表不为空
			printk("VFS: Busy inodes after unmount of %s. "
			   "Self-destruct in 5 seconds.  Have a nice day...\n",
			   sb->s_id);
		}
		put_fs_excl(); //递减当前进程的fs_excl引用计数
	}
	spin_lock(&sb_lock);
	list_del_init(&sb->s_instances); //从同类型文件系统链表中移除
	spin_unlock(&sb_lock);
	up_write(&sb->s_umount);
}
    2、使用init_mount_tree函数挂载根文件系统rootfs(实际上是创建一个属于根文件系统的struct  vfsmount实例),源代码如下所示:
/* fs/namespace.c */
static void __init init_mount_tree(void)
{
	struct vfsmount *mnt;
	struct mnt_namespace *ns;
	struct path root;

	mnt = do_kern_mount("rootfs", 0, "rootfs", NULL); //挂载根文件系统
	if (IS_ERR(mnt))
		panic("Can't create rootfs");

	ns = create_mnt_ns(mnt); //创建mnt命名空间
	if (IS_ERR(ns))
		panic("Can't allocate initial namespace");

	init_task.nsproxy->mnt_ns = ns; //初始进程指向该命名空间
	get_mnt_ns(ns); //递增ns->count引用计数

	root.mnt = ns->root; //根文件系统
	root.dentry = ns->root->mnt_root; //根目录对应的目录项
	
	//初始进程的根目录和当前目录都设置为root
	set_fs_pwd(current->fs, &root);
	set_fs_root(current->fs, &root);
}
    2.1、挂载操作所涉及的数据如下所示:
/* include/linux/mount.h */
struct mnt_pcp {
	int mnt_count;
	int mnt_writers;
};

struct vfsmount {
	struct list_head mnt_hash;		//用于插入链式哈希表mount_hashtable
	struct vfsmount *mnt_parent;	//父文件系统,即当前文件系统将在其中挂载的文件系统
	struct dentry *mnt_mountpoint;	//挂载点目录项,属于父文件系统
	struct dentry *mnt_root;		//当前文件系统的根目录项,与mnt_mountpoint目录项表示同一目录
	struct super_block *mnt_sb;		//指向当前文件系统的超级块
#ifdef CONFIG_SMP
	struct mnt_pcp __percpu *mnt_pcp; //与非SMP系统中那两个成员的含义一样,只不过这里是以per-cpu变量的形式使用
	atomic_t mnt_longterm;	//长期存在的struct vfsmount实例的计数
#else
	int mnt_count;		//引用计数
	int mnt_writers;	//写访问计数
#endif
	struct list_head mnt_mounts;	//所有子文件系统
	struct list_head mnt_child;		//用于插入父文件系统的mnt_mounts链表
	int mnt_flags;					//挂载标志
	/* 4 bytes hole on 64bits arches without fsnotify */
#ifdef CONFIG_FSNOTIFY //vfsmount层级的文件系统监测机制
	__u32 mnt_fsnotify_mask; //事件掩码
	struct hlist_head mnt_fsnotify_marks; //struct fsnotify_vfsmount_mark类型的链表
#endif
	const char *mnt_devname;		//设备名,如/dev/dsk/hda1
	struct list_head mnt_list;		//用于插入到所属命名空间的所有挂载实例的链表中
	struct list_head mnt_expire;	//用于特定文件系统的自动过期链表的元素
	//共享子树挂载选项分为共享挂载、从属挂载、不可绑定挂载和私有挂载四种
	struct list_head mnt_share;			//共享挂载循环链表
	struct list_head mnt_slave_list;	//所属的从属挂载链表
	struct list_head mnt_slave;			//用于插入其主挂载的mnt_slave_list链表中
	struct vfsmount *mnt_master;		//指向它的主挂载
	struct mnt_namespace *mnt_ns; 		//所属的命名空间
	int mnt_id;			//ID号,可以根据它看出文件系统的层次关系,可在/proc/(进程ID)/mountinfo文件里查看
	int mnt_group_id;		//共享挂载组ID或从属挂载的主挂载的组ID
	int mnt_expiry_mark;	//标记自动过期
	int mnt_pinned;
	int mnt_ghosts;
};
    struct  vfsmount用于在内核中表示一个已挂载的文件系统实例。通过它系统中所有的文件系统就可以形成一个挂载树,也就形成了一个目录树,因此通过根文件系统就可以遍历系统中所有的文件系统及其所有的文件或目录。须要特别强调的是,每个挂载点在内核中都对应着两个目录项,一个属于被挂载的文件系统,另一个则属于当前文件系统根目录所对应的目录项。

    2.2、create_mnt_ns函数用于创建一个mnt命名空间,并将根文件系统与该命名空间关联起来,源代码如下所示:

/* include/linux/mnt_namespace.h */
struct mnt_namespace {
	atomic_t		count;		//引用计数
	struct vfsmount *	root;	//根文件系统的挂载实例
	struct list_head	list;	//该命名空间下所有挂载实例的链表
	wait_queue_head_t poll;		//等待队列
	int event;
};

/* fs/namespace.c */
static struct mnt_namespace *alloc_mnt_ns(void)
{
	struct mnt_namespace *new_ns;

	new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
	if (!new_ns)
		return ERR_PTR(-ENOMEM);
	atomic_set(&new_ns->count, 1);
	new_ns->root = NULL;
	INIT_LIST_HEAD(&new_ns->list);
	init_waitqueue_head(&new_ns->poll);
	new_ns->event = 0;
	return new_ns;
}

struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
{
	struct mnt_namespace *new_ns;

	new_ns = alloc_mnt_ns(); //分配内存并初始化所有成员
	if (!IS_ERR(new_ns)) { //成功返回
		mnt->mnt_ns = new_ns; //设置该文件系统所属的命名空间
		__mnt_make_longterm(mnt); //递增mnt->mnt_longterm引用计数
		new_ns->root = mnt; //命名空间的根文件系统
		//这里应该写成list_add(&new_ns->root->mnt_list, &new_ns->list)才比较好理解
		list_add(&new_ns->list, &new_ns->root->mnt_list);
	}
	return new_ns;
}
    2.3、do_kern_mount函数主要用于生成根文件系统rootfs的struct  vfsmount实例,源代码如下所示:
/* fs/super.c */
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
	struct file_system_type *type = get_fs_type(fstype); //根据名称获得文件系统类型
	struct vfsmount *mnt;
	if (!type)
		return ERR_PTR(-ENODEV);
	mnt = vfs_kern_mount(type, flags, name, data);
	if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
	    !mnt->mnt_sb->s_subtype) //尚未在超级块保存子文件类型名称
		mnt = fs_set_subtype(mnt, fstype); //将名称中.字符之后的所有字符保存在超级块的mnt->mnt_sb->s_subtype成员中
	put_filesystem(type);
	return mnt;
}
    (1)、调用get_fs_type函数根据文件系统名称来获得文件系统的类型信息,源代码如下所示:
/* fs/filesystems.c */
static DEFINE_RWLOCK(file_systems_lock);

struct file_system_type *get_fs_type(const char *name)
{
	struct file_system_type *fs;
	const char *dot = strchr(name, '.');
	int len = dot ? dot - name : strlen(name); //如果名称中存在.字符则文件长度为.之前所有字符的长度

	fs = __get_fs_type(name, len);
	if (!fs && (request_module("%.*s", len, name) == 0)) //这里根据fs为空指针可以判断该文件系统是以模块形式加载的
		fs = __get_fs_type(name, len);

	if (dot && fs && !(fs->fs_flags & FS_HAS_SUBTYPE)) { //名称中包含.字符但又未配置FS_HAS_SUBTYPE标志
		put_filesystem(fs);
		fs = NULL;
	}
	return fs;
}

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)) //从文件系统类型链表中找到该文件系统但模块状态为MODULE_STATE_GOING
		fs = NULL;
	read_unlock(&file_systems_lock);
	return fs;
}
    (2)、调用vfs_kern_mount函数生成rootfs文件系统的struct  vfsmount实例,并调用该类文件系统的mount函数来构建超级块、根目录项及其i节点,然后将返回的根目录项与所生成的struct  vfsmount实例关联起来。源代码如下所示:
/* fs/super.c */
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
	struct vfsmount *mnt;
	struct dentry *root;
	char *secdata = NULL;
	int error;

	if (!type)
		return ERR_PTR(-ENODEV);

	error = -ENOMEM;
	mnt = alloc_vfsmnt(name); //分配struct vfsmount内存并初始化
	if (!mnt)
		goto out;

	if (flags & MS_KERNMOUNT)
		mnt->mnt_flags = MNT_INTERNAL; //MNT_INTERNAL表示内核所挂载

	if (data && !(type->fs_flags & FS_BINARY_MOUNTDATA)) {
		secdata = alloc_secdata(); //为安全检查模块将要使用的数据分配内存
		if (!secdata)
			goto out_mnt;

		error = security_sb_copy_data(data, secdata); //安全检查模块
		if (error)
			goto out_free_secdata;
	}
	
	//struct file_system_type中的mount和get_sb函数只须实现其中之一即可
	if (type->mount) { 
		root = type->mount(type, flags, name, data); //特定文件系统的挂载函数
		if (IS_ERR(root)) {
			error = PTR_ERR(root);
			goto out_free_secdata;
		}
		mnt->mnt_root = root; //关联根目录项
		mnt->mnt_sb = root->d_sb; //关联超级块
	} else {
		error = type->get_sb(type, flags, name, data, mnt);
		if (error < 0)
			goto out_free_secdata;
	}
	BUG_ON(!mnt->mnt_sb);
	WARN_ON(!mnt->mnt_sb->s_bdi);
	WARN_ON(mnt->mnt_sb->s_bdi == &default_backing_dev_info);
	
	mnt->mnt_sb->s_flags |= MS_BORN; //设置相关超级块的MS_BORN标志

	error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata); //安全检查模块
	if (error)
		goto out_sb;
		
	//超过了s_maxbytes变量的数据类型所能表示的最大正整数
	WARN((mnt->mnt_sb->s_maxbytes < 0), "%s set sb->s_maxbytes to "
		"negative value (%lld)\n", type->name, mnt->mnt_sb->s_maxbytes);

	mnt->mnt_mountpoint = mnt->mnt_root; //如果是根文件系统则指向自己的根目录项,否则指向挂载点在父文件系统的目录项
	mnt->mnt_parent = mnt; //如果是根文件系统则指向自身,否则指向父文件系统
	up_write(&mnt->mnt_sb->s_umount);
	free_secdata(secdata);
	return mnt;
out_sb:
	dput(mnt->mnt_root);
	deactivate_locked_super(mnt->mnt_sb);
out_free_secdata:
	free_secdata(secdata);
out_mnt:
	free_vfsmnt(mnt);
out:
	return ERR_PTR(error);
}
    根文件系统rootfs除了一个根目录外,没有包含其他任何内容,实际上是提供了一个挂载点,以便于在内核启动完成之后实际的文件系统(如ext4、yaffs2等)挂载其上。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tanglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值