参考:Linux Filesystem: 解析 Linux 中的 VFS 文件系统机制

主要代码,

init_rootfs();
init_mount_tree();

1.init_rootfs()解析

init_rootfs()
-->bdi_init(&ramfs_backing_dev_info);
/*
注册过程实际上将表示各实际文件系统的 struct file_system_type 数据结构的实例化,
将rootfs_fs_type加入到static struct file_system_type *file_systems为首的单链表。
*/
-->register_filesystem(&rootfs_fs_type);

struct backing_dev_info结构是显示设备信息的描述符,定义如下:

static struct backing_dev_info ramfs_backing_dev_info = {
.ra_pages = , /* No readahead */
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK |
BDI_CAP_MAP_DIRECT | BDI_CAP_MAP_COPY |
BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP,
};

2.init_mount_tree()解析

init_mount_tree() 这个函数为 VFS 建立了根目录 "/"

init_mount_tree()
-->struct vfsmount *mnt = do_kern_mount("rootfs", , "rootfs", NULL);
/*
1.根据“rootfs”在static struct file_system_type *file_systems链表中
找到代表文件系统的file_system_type结构体
*/
-->struct file_system_type *type = get_fs_type("rootfs");
-->struct vfsmount *mnt = vfs_kern_mount(type, , "rootfs", NULL);
/*2.从mnt_cache缓存分配vfsmount结构体并初始化*/
-->struct vfsmount *mnt = alloc_vfsmnt(name);
-->type->get_sb(type, , "rootfs", NULL, mnt);
即rootfs_get_sb(type, , "rootfs", NULL, mnt);
-->get_sb_nodev(type, MS_NOUSER, NULL, ramfs_fill_super,mnt);
-->struct super_block *s = sget(type, NULL, set_anon_super, NULL);
/*2.分配并初始化super_block结构体*/
-->struct super_block *s = alloc_super(type);
-->set(s, NULL);
即set_anon_super(s,NULL)
/*设置super_block结构体的s_dev成员*/
-->s->s_dev = MKDEV(, dev & MINORMASK);
-->s->s_type = type;
-->strlcpy(s->s_id, type->name, sizeof(s->s_id));
/*系统中所有的super_block结构体都连接到super_blocks为首的list_head链表*/
-->list_add_tail(&s->s_list, &super_blocks);
/*属于file_system_type指定文件系统的super_block结构体都连接到type->fs_supers链表*/
-->list_add(&s->s_instances, &type->fs_supers);
-->s->s_flags = MS_NOUSER;
-->fill_super(s, NULL, )
即ramfs_fill_super(s, NULL, )
-->sb->s_op = &ramfs_ops;
/*3.分配inode结构体并初始化*/
-->struct inode *inode = ramfs_get_inode(sb, S_IFDIR | fsi->mount_opts.mode, );
-->struct inode * inode = new_inode(sb);
-->设置inode成员
/*4.分配dentry并初始化,“/”*/
-->struct dentry *root = d_alloc_root(inode);
-->static const struct qstr name = { .name = "/", .len = }
-->struct dentry *res = d_alloc(NULL, &name);
-->res->d_sb = inode->i_sb;
-->res->d_parent = res;
-->d_instantiate(res, inode);
-->list_add(&res->d_alias, &inode->i_dentry);
-->res->d_inode = inode;
-->s->s_flags |= MS_ACTIVE;
-->simple_set_mnt(mnt, s);
-->mnt->mnt_sb = s;
-->mnt->mnt_root = dget(sb->s_root);
mnt->mnt_mountpoint = mnt->mnt_root;
mnt->mnt_parent = mnt;
/*
为系统最开始的进程(即 init_task 进程)准备它的进程数据块中的namespace 域,
主要目的是将 do_kern_mount() 函数中建立的 mnt 和 dentry 信息记录在了 init_task 进程的进程数据块中,
这样所有以后从 init_task 进程 fork 出来的进程也都先天地继承了这一信息
*/
-->struct mnt_namespace *ns = kmalloc(sizeof(*ns), GFP_KERNEL);
-->list_add(&mnt->mnt_list, &ns->list);
-->ns->root = mnt;
-->mnt->mnt_ns = ns;
-->struct path root.mnt = ns->root;
struct path root.dentry = ns->root->mnt_root;
-->set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);

3.在根目录下增加目录

Linux 下用系统调用 sys_mkdir 来在 VFS 目录树中增加新的节点。为配合路径搜索,引入了下面一个数据结构:

struct path {
struct vfsmount *mnt;
struct dentry *dentry;
};
/*
这个数据结构在路径搜索的过程中用来记录相关信息,起着类似"路标"的作用
其中前path.dentry记录的是要建目录的父目录的信息
last,flags,last_type三项记录的是所查找路径的最后一个节点(即待建目录或文件)的信息。
*/
struct nameidata {
struct path path;
struct qstr last;
unsigned int flags;
int last_type;
unsigned depth;
char *saved_names[MAX_NESTED_LINKS + ]; /* Intent data */
union {
struct open_intent open;
} intent;
};

4.在 VFS 树中挂载文件系统

这一过程可简单描述为:将某一设备(dev_name)上某一文件系统(file_system_type)安装到VFS目录树上的某一安装点(dir_name)。它要解决的问题是:将对 VFS 目录树中某一目录的操作转化为具体安装到其上的实际文件系统的对应操作。

比如说,如果将 hda2 上的根文件系统(假设文件系统类型为 ext2)安装到 "/dev" 目录上(此时,"/dev" 目录就成为了安装点),那么安装成功之后应达到这样的目的,即:对 VFS 文件系统的 "/dev" 目录执行 "ls" 指令,该条指令应能列出 hda2 上 ext2 文件系统的根目录下所有的目录和文件。

记住:对目录或文件的操作将最终由目录或文件所对应的 inode 结构中的 i_op 和 i_fop 所指向的函数表中对应的函数来执行。所以,不管最终解决方案如何,都可以设想必然要通过将对 "/dev" 目录所对应的 inode 中 i_op 和 i_fop 的调用转换到hda2 上根文件系统 ext2 中根目录所对应的 inode 中 i_op 和 i_fop 的操作。

初始过程由 sys_mount() 系统调用函数发起,该函数原型声明如下:
long sys_mount(char * dev_name, char * dir_name, char * type,unsigned long flags, void * data);
参数 char *type 为标识将要安装的文件系统类型字符串,对于 ext2 文件系统而言,就是"ext2"。

为了更好地理解这一过程,用一个具体的例子来说明:我们准备将来自主硬盘第 2 分区(hda2)上的 ext2 文件系统安装到前面创建的 "/dev" 目录中。那么对于 sys_mount() 函数的调用便具体为:

sys_mount("hda2","/dev ","ext2",…)
/*
将来自用户内存空间(user space)的参数拷贝到内核空间后,
便调用 do_mount() 函数开始真正的安装文件系统的工作
*/
-->do_mount()
-->/*调用 path_lookup() 函数来得到安装点的相关信息,
如同创建目录过程中叙述的那样,该安装点的信息最终记录在 struct nameidata 类型的一个变量当中,
为叙述方便,记该变量为nd
*/
/*调用 do_add_mount() 函数来向 VFS 树中安装点 "/dev " 安装一个实际的文件系统。*/
-->do_add_mount()
/*建立一新的安装区域块*/
-->do_kern_mount()
-->graft_tree()
-->将 do_kern_mount() 函数返回的一 struct vfsmount 类型的变量加入到安装系统链表中
-->将新分配的 struct vfsmount 类型的变量加入到一个hash表中

rootfs注册挂载过程分析的更多相关文章

  1. 从linux启动到rootfs的挂载分析

    简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统.在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系 ...

  2. Linux--根文件系统的挂载过程分析

    前言: 本篇文章以S3C6410公版的Linux BSP和U-Boot来进行分析,文中全部提及的名词和数据都是以该环境为例,全部的代码流程也是以该环境为例来进行分析.哈哈.假设有不对或者不完好的地方, ...

  3. tiny4412 busybox制作根文件系统rootfs nfs 挂载 ubuntu 14.04

    http://blog.csdn.net/liudijiang/article/details/50555429(转) 首先得要有制作好的uboot和linux内核镜像zImage,先烧录到sd卡里, ...

  4. 文件系统的挂载(2)---挂载rootfs文件系统

    一.目的 本文主要讲述linux内核rootfs文件系统的挂载过程,内核版本为3.10. rootfs是基于内存的文件系统,没有实际的存储设备,所有操作都在内存中完成.为了保证linux内核的精简性, ...

  5. linux文件系统初始化过程(2)---挂载rootfs文件系统

    一.目的 本文主要讲述linux3.10文件系统初始化过程的第一阶段:挂载rootfs文件系统. rootfs是基于内存的文件系统,所有操作都在内存中完成:也没有实际的存储设备,所以不需要设备驱动程序 ...

  6. linux根文件系统的挂载过程详解

    一:前言 前段时间在编译kernel的时候发现rootfs挂载不上.相同的root选项设置旧版的image却可以.为了彻底解决这个问题.研究了一下rootfs的挂载过程.特总结如下,希望能给这部份知识 ...

  7. create rootfs.img using loop device

    reference: https://www.thegeekdiary.com/how-to-create-virtual-block-device-loop-device-filesystem-in ...

  8. 使用jquery的on方法注册事件遇到的坑

    1,使用on注册事件 $(selector).on(event,childSelector,data,function) 2,$(selector)中的selector可以是document,那么意味 ...

  9. Aufs与Devicemapper的关系

    Aufs与Devicemapper的应用 Aufs是Docker最初采用的文件系统,由于Aufs未能加入到Linux内核,考虑到兼容性问题,加入了Devicemapper的支持.目前,除少数版本如Ub ...

随机推荐

  1. web基础笔记

    浏览器渲染页面的过程 浏览器渲染页面前需要先构建 DOM 和 CSSOM 树.因此,我们需要确保尽快将 HTML 和 CSS 都提供给浏览器. 参考:https://developers.google ...

  2. 如何在spring环境中做单元测试

    在测试类的上方加入以下注解 @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:spring.xm ...

  3. 我的NopCommerce之旅(9): 编写Plugin实例

    一.基础介绍 ——In computing, a plug-in (or plugin) is a set of software components that add specific abili ...

  4. ubuntu 文件解压命令

    [解压.zip文件] unzip ./FileName.zip //前提是安装了unzip 安装unzip命令:sudo apt-get install unzip 卸载unzip软件 命令:sudo ...

  5. WPF CanExecuteChanged

    继承ICommand ,RelayCommand命令 public class RelayCommand : ICommand { private readonly Action _execute; ...

  6. C#入门笔记1

    C#是用于创建要运行在.NET CLR上的应用程序的语言之一,从C和C++语言演化而来,是微软专门为使用.NET平台而创建的.优点:就是彻头彻尾为.NET Framework设计语言. C#能编写什么 ...

  7. LaTeX入门简介

    原创链接 http://blog.csdn.net/perfumekristy/article/details/8515272 1.LaTeX软件的安装和使用 方法A(自助):在MikTeX的官网下载 ...

  8. 安装MySQLdb出现HAVE_WCSCOLL重定义问题的解决方法

    root@wodeyitian MySQL-python-1.2.3]# python setup.py install running install running bdist_egg runni ...

  9. ubuntu kylin 13.10 无法安装ia32-libs解决方案

    1.安装 Synaptic 2.sudo apt-get install synaptic 3.进入synaptic ,设置->软件库 4.点击  其他软件->添加 5.输入“deb ht ...

  10. 100行代码让您学会JavaScript原生的Proxy设计模式

    面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了.比如我之前写过代理模式在Java中实现的两篇文章: Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理 J ...