rootfs注册挂载过程分析
参考: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注册挂载过程分析的更多相关文章
- 从linux启动到rootfs的挂载分析
简单的来说,根文件系统包括虚拟根文件系统和真实根文件系统.在Kernel启动的初始阶段,首先去创建虚拟的根文件系统,接下来再去调用do_mount来加载真正的文件系统,并将根文件系统切换到真正的文件系 ...
- Linux--根文件系统的挂载过程分析
前言: 本篇文章以S3C6410公版的Linux BSP和U-Boot来进行分析,文中全部提及的名词和数据都是以该环境为例,全部的代码流程也是以该环境为例来进行分析.哈哈.假设有不对或者不完好的地方, ...
- tiny4412 busybox制作根文件系统rootfs nfs 挂载 ubuntu 14.04
http://blog.csdn.net/liudijiang/article/details/50555429(转) 首先得要有制作好的uboot和linux内核镜像zImage,先烧录到sd卡里, ...
- 文件系统的挂载(2)---挂载rootfs文件系统
一.目的 本文主要讲述linux内核rootfs文件系统的挂载过程,内核版本为3.10. rootfs是基于内存的文件系统,没有实际的存储设备,所有操作都在内存中完成.为了保证linux内核的精简性, ...
- linux文件系统初始化过程(2)---挂载rootfs文件系统
一.目的 本文主要讲述linux3.10文件系统初始化过程的第一阶段:挂载rootfs文件系统. rootfs是基于内存的文件系统,所有操作都在内存中完成:也没有实际的存储设备,所以不需要设备驱动程序 ...
- linux根文件系统的挂载过程详解
一:前言 前段时间在编译kernel的时候发现rootfs挂载不上.相同的root选项设置旧版的image却可以.为了彻底解决这个问题.研究了一下rootfs的挂载过程.特总结如下,希望能给这部份知识 ...
- create rootfs.img using loop device
reference: https://www.thegeekdiary.com/how-to-create-virtual-block-device-loop-device-filesystem-in ...
- 使用jquery的on方法注册事件遇到的坑
1,使用on注册事件 $(selector).on(event,childSelector,data,function) 2,$(selector)中的selector可以是document,那么意味 ...
- Aufs与Devicemapper的关系
Aufs与Devicemapper的应用 Aufs是Docker最初采用的文件系统,由于Aufs未能加入到Linux内核,考虑到兼容性问题,加入了Devicemapper的支持.目前,除少数版本如Ub ...
随机推荐
- Java内置锁和简单用法
一.简单的锁知识 关于内置锁 Java具有通过synchronized关键字实现的内置锁,内置锁获得锁和释放锁是隐式的,进入synchronized修饰的代码就获得锁,走出相应的代码就释放锁. jav ...
- 086 Partition List 分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前.你应当保留两个分区中每个节点的初始相对位置.例如,给定1->4->3->2-&g ...
- mvc 连接数据库但单复值得问题
1. The model backing the ‘MusicStoreDBContext‘ context has changed since the database was created. ...
- webpack(1)
在网页中会引用哪些常见的静态资源? JS .js .jsx .coffee .ts(TypeScript 类 C# 语言) CSS .css .less .sass .scss Images .jpg ...
- CF1072B Curiosity Has No Limits
思路: 对于序列t,只要第一个数确定了,后续的数也随之确定了.枚举四种情况即可.实现: #include <iostream> #include <vector> using ...
- arcgis jsapi接口入门系列(4):用代码在地图画点线面
PS:用代码画点这样写是为了跟后面的用鼠标画点线面区分出来 画点 drawPointGraphic: function () { //点有多种样式:一般的点,显示文字,显示图片 //一般的点 let ...
- 企业CIO、CTO必读的34个经典故事
一. 用人之道 去过庙的人都知道,一进庙门,首先是弥陀佛,笑脸迎客,而在他的北面,则是黑口黑脸的韦陀.但相传在很久以前,他们并不在同一个庙里,而是分别掌管不同的庙.弥乐佛热情快乐,所以来的人非常多,但 ...
- (八)maven学习之继承
继承 如果项目划分了多个模块,都需要依赖相似的jar包,只需要创建一个父模块,在它的pom.xml文件中配置依赖的jar包.功能模块只需要继承父模块,就可以自动得到其依赖的jar包,而不需要再每个模块 ...
- ubuntu16.0 安装 openstack
主要参考官方文档:https://docs.openstack.org/liberty/zh_CN/install-guide-ubuntu/environment-nosql-database.ht ...
- 纪念一下我的第一个php扩展
C扩展代码 生成 so扩展文件( 很多文章介绍 生成so时候 喜欢用 # phpize ./configure ...... 刚开始掉进坑里面出不来 就是因为把这两个命令看成了一个 phpize ./ ...