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

2023-12-03 12:38

本文主要是介绍根文件系统挂载过程—基于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方法。

<fs/ramfs/inode.c>
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 }

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

<fs/ramfs/inode.c>
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的方式挂载该文件系统。一种文件系统只能注册一次,但是多个设备可以挂载为同一种文件系统类型的设备。

<fs/namespace.c>
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层自己的超级块。

<fs/super.c>
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);
}

这篇关于根文件系统挂载过程—基于linux3.10的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/449476

相关文章

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

MySQL中的InnoDB单表访问过程

《MySQL中的InnoDB单表访问过程》:本文主要介绍MySQL中的InnoDB单表访问过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、访问类型【1】const【2】ref【3】ref_or_null【4】range【5】index【6】

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左

CSS3打造的现代交互式登录界面详细实现过程

《CSS3打造的现代交互式登录界面详细实现过程》本文介绍CSS3和jQuery在登录界面设计中的应用,涵盖动画、选择器、自定义字体及盒模型技术,提升界面美观与交互性,同时优化性能和可访问性,感兴趣的朋... 目录1. css3用户登录界面设计概述1.1 用户界面设计的重要性1.2 CSS3的新特性与优势1.

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte