根文件系统挂载过程—基于linux3.10

本文基于linux3.10某一嵌入式系统,该文件系统的配置选项设置如下:


图1.1 根文件系统配置选项设置

         两行配置如下:

[*] Initial RAMfilesystem and RAM disk (initramfs/initrd) support

(usr/rootfs.cpio.gz)Initramfs source file(s)

这两行的意义是启用initramfs文件系统,并且将源码目录下的usr/roorfs.cpio.gz作为根文件系统。内核会将该文件系统加载到内存并且挂载该文件系统做为根文件系统。

在windos下通常有C盘、D盘之类的分区,可以把linux下的根文件系统“/”理解成C盘这样的设备,对于这里所说的嵌入式情景,这个设备就是一整块NAND FLASH,看起来有点类似图1.1所示的情况,在挂载根文件系统时会调用rootfs_mount读取flash上的超级块信息,并依据该信息构建VFS层的超级块信息。超级块信息获取是通过MTD层接口实现的,实际上底层是通过CPU 的NAND FLASH控制器来实现对NAND FLASH读写的。


图1.2  只有一个根节点的NAND设备

在《 Linux系统启动那些事—基于Linux 3.10内核》和《虚拟文件系统 (VFS)-基于linux3.10》分别对启动加载流程和文件系统的挂载有过叙述,这里一些切入点源于这两篇文章。

首先是start_kernel()函数,该函数在启动那些事中有过说明,该函数调用vfs_caches_init()函数,接下来调用inode_init()初始化索引节点,然后调用mnt_init()函数挂载根文件系统,mount_init()函数调用init_rootfs函数。

图1.3 根文件系统挂载函数调用流程

调用init_rootfs函数并未传递参数,所以分析起来容易很多。bdi是backing_dev_info的缩写,bdi_init用于初始化后备存储器的一些字段,这些字段包括回写链表、回写锁等,关系到读写策略,和挂载关系并不大。register_filesystem在《虚拟文件系统 (VFS)-基于linux3.10》明确指出一个文件系统想要使用必须先加载该文件系统,表示该文件系统的参数rootfs_fs_type包括了该文件文件系统的mount方法。


277 int __init init_rootfs(void)
278 {
279     int err;
280     err = bdi_init(&ramfs_backing_dev_info);
281     if (err)
282         return err;
283         
284     err = register_filesystem(&rootfs_fs_type);
285     if (err)
286         bdi_destroy(&ramfs_backing_dev_info);
287         
288     return err;
289 }

对应这里的根文件系统的结构定义如下:


static struct file_system_type rootfs_fs_type = {.name		= "rootfs",.mount		= rootfs_mount,.kill_sb	= kill_litter_super,
};

init_mount_tree()也没有需要传递的参数,get_fs_type函数在VFS那篇文章提过,这里就是获得参数“rootfs”对应的文件系统类型,如果发现当前文件系统并未注册该文件系统则会尝试以module的方式挂载该文件系统。一种文件系统只能注册一次,但是多个设备可以挂载为同一种文件系统类型的设备。


2686 static void __init init_mount_tree(void)
2687 {
2688     struct vfsmount *mnt;
2689     struct mnt_namespace *ns;
2690     struct path root;
2691     struct file_system_type *type;
2692 
2693     type = get_fs_type("rootfs");
2694     if (!type)
2695         panic("Can't find rootfs type");
2696     mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
2697     put_filesystem(type);
2698     if (IS_ERR(mnt))
2699         panic("Can't create rootfs");
2700         
2701     ns = create_mnt_ns(mnt);
2702     if (IS_ERR(ns))
2703         panic("Can't allocate initial namespace");
2704         
2705     init_task.nsproxy->mnt_ns = ns;
2706     get_mnt_ns(ns);
2707     
2708     root.mnt = mnt;
2709     root.dentry = mnt->mnt_root;
2710     
2711     set_fs_pwd(current->fs, &root);
2712     set_fs_root(current->fs, &root);
2713 }

2694行判断是否找到了该文件系统,因为后面mount根文件系统时是需要其mount方法的,而mount方法存在于文件系统类型中,所以2693行必须要先查找一下,如果没找到则2695行就显示panic,这是内核非常严重的错误,这种错误预示着设备将宕机。2696行是实际意义上的挂载。

struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{struct mount *mnt;struct dentry *root;if (!type)return ERR_PTR(-ENODEV);
/*分配并初始化一个mount结构体,该结构体存储了若干和mount相关的信息,这些信息包括,挂载点(目录项)、父挂载点、若干挂载链表等*/mnt = alloc_vfsmnt(name); if (!mnt)return ERR_PTR(-ENOMEM);if (flags & MS_KERNMOUNT)mnt->mnt.mnt_flags = MNT_INTERNAL;root = mount_fs(type, flags, name, data); // 真正的挂载函数,如果挂载成功,则会返回目录项if (IS_ERR(root)) {free_vfsmnt(mnt);return ERR_CAST(root);}mnt->mnt.mnt_root = root; //挂载点目录项printk(KERN_EMERG "mnt->mnt.mnt_root:%s", root->d_iname);mnt->mnt.mnt_sb = root->d_sb;//挂载点超级块mnt->mnt_mountpoint = mnt->mnt.mnt_root;mnt->mnt_parent = mnt;//父目录信息br_write_lock(&vfsmount_lock);
//将新挂载的根文件系统添加到超级块的挂载链表上list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);br_write_unlock(&vfsmount_lock);return &mnt->mnt;
}

mount_fs的工作就是完成根文件系统的挂载,实际上就是读取FLASH上超级块并填充VFS层自己的超级块。


1086 struct dentry *
1087 mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
1088 {
1089     struct dentry *root;
1090     struct super_block *sb;
1091     char *secdata = NULL;
1092     int error = -ENOMEM;
…
1103 //这里就是调用根文件系统的mount方法。
1104     root = type->mount(type, flags, name, data);
1105     if (IS_ERR(root)) {
1106         error = PTR_ERR(root);
1107         goto out_free_secdata;
1108     }
1109     sb = root->d_sb;
…
1137     return ERR_PTR(error);
1138 }

rootfs_mount 函数实际上就是对mount_nodev的封装,很容易理解这里命名方法,因为该文件系统是没有实际的物理设备对应的,其实际上只存在于内存中。最后一个参数ramfs_fill_super指定了超级块的填充方法。

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);
}
mount_nodev首先调用sget获得超级块,然后调用指针函数参数fill_super对其进行填充。
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, flags, NULL);if (IS_ERR(s))return ERR_CAST(s);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);
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部