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

article/2025/9/19 19:02:12

本文基于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);
}

http://chatgpt.dhexx.cn/article/gYHbQ1tr.shtml

相关文章

JFFS2根文件系统制作

一.根文件系统简介 根文件系统首先是内核启动时所mount的第一个文件系统&#xff0c;内核代码映像文件保存在根文件系统中&#xff0c;系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本&#xff08;如inittab、rcS&#xff09;和服务加载到内存中去运行。 文件系统…

ARM Linux启动流程-根文件系统的加载

前言 在Kernel启动的初始阶段&#xff0c;首先去创建虚拟的根文件系统(rootfs)&#xff0c;接下来再去调用do_mount来加载真正的文件系统&#xff0c;并将根文件系统切换到真正的文件系统&#xff0c;也即真实的文件系统。   接下来结核内核代码(内核版本&#xff1a;linux-…

怎么使用 Buildroot 构建根文件系统

1. Buildroot 简介 制作根文件系统有归多种方法&#xff1a; ① 使用Busybox手工制作 Busybox本身包含了很了Linux命令&#xff0c;但是要编译其他程序的话需要手工下载、编译&#xff0c;如果它需要某些依赖库&#xff0c;你还需要手工下载、编译这些依赖库。 如果想做一个极…

一文讲解Linux内核中根文件系统挂载流程

根文件系统的概念 根文件系统是控制权从linux内核转移到用户空间的一个桥梁。linux内核就类似于一个黑匣子&#xff0c;只向用户提供各种功能的接口&#xff0c;但是功能的具体实现不可见&#xff0c;用户程序通过对这些功能接口的不同整合实现不同的功能需求。以用户的角度来…

【linux kernel】挂载根文件系统之rootfs

挂载根文件系统之rootfs 文章目录 挂载根文件系统之rootfs一、开篇二、rootfs根文件系统&#xff08;2-1&#xff09;初始化rootfs&#xff08;2-2&#xff09;挂载rootfs文件系统&#xff08;2-3&#xff09;创建简单的rootfs根文件系统目录和文件&#xff08;2-4&#xff09;…

2021年Linux技术总结(三):根文件系统(rootfs)

# 一、根文件系统简介 Linux系统三大块&#xff1a;U-boot、kernel以及最后这个rootfs&#xff0c;跟文件系统。在kernel中&#xff0c;启动流程的最后会调用 prepare_namespace 函数&#xff0c;挂载根文件系统&#xff0c;这里就是挂载的本篇要说的。 根文件系统保存了内核代…

详解制作根文件系统

目录 前言 具体步骤 一.编译&#xff0f;安装busybox&#xff0c;生成/bin、/sbin、/usr/bin、/usr/sbin目录 二.利用交叉编译工具链&#xff0c;构建/lib目录 三.手工构建/etc目录 四.手工构建最简化的/dev目录 五.创建其它空目录 六.配置系统自动生成/proc目录和构建…

根文件系统理解

文件系统概念 文件系统是一些代码&#xff0c;是一套软件&#xff0c;这套软件的功能就是对存储设备的扇区进行管理&#xff0c;将这些扇区的访问变成了对目录和文件名的访问。我们在上层按照特定的目录和文件名去访问一个文件时&#xff0c;文件系统会将这个目录文件名转换成对…

linux之根文件系统

前言 1、板卡上电后首先由UBOOT启动初始化板卡&#xff0c;将Linux内核移到内存中运行 2、由linux内核自行做了初始化等操作&#xff0c;挂在了第一个应用程序上&#xff08;根文件系统/linuxrc&#xff09; 3、根文件系统会提供磁盘管理服务&#xff0c;glibc设备节点&…

Linux:根文件系统构建

文章目录 一、编译 BusyBox 构建根文件系统1.创建BusyBox路径并解压2.修改顶层Makefile3.修改 busybox 源码4.配置busybox5.编译busybox6.向 rootfs 的“/lib”目录添加库文件7.向 rootfs 的“usr/lib”目录添加库文件创建其他文件夹 二、NFS挂载根文件系统1.bootargs 环境变量…

制作嵌入式Linux根文件系统

文章目录 1. 根文件系统布局2. 使用BusyBox生成二进制工具2-1. 获取BusyBox源码2-2. 配置BusyBox2-2-1. 选择编译静态库2-2-2. 选择交叉编译工具链2-2-3. 选择安装目录2-2-4. 编译安装 3. 构建根文件系统3-1. 完善目录结构3-2. 添加C运行库文件3-3. 添加初始化配置脚本3-3-1. 修…

[架构之路-30]:目标系统 - 系统软件 - Linux OS根文件系统rootfs的概念、组成、制作以及用busybox制作根文件系统

目录 前言&#xff1a; 第1章 什么是根文件系统 1.1 什么是文件 1.2 什么是文件系统 1.3 文件系统组织文件的方式&#xff1a;树形结构 1.4 统一的虚拟文件系统 1.5 物理存储介质与物理文件系统类型 1.5 什么是根文件系统 第2章 根文件系统的标准结构 2.1 根文件系统…

安装Ubuntu 16.04时出现:没有定义根文件系统,请到分区菜单修改

在安装Ubuntu 16.04时&#xff0c;尤其是选项空闲硬盘新建分区安装时&#xff0c;容易出现这种情况&#xff0c;这个是由于没有配置挂载点导致的&#xff0c;解决方法如下&#xff1a; 在挂在点输入“&#xff0f;”。 原理&#xff1a; Linux和Windows的文件系统不一样&#x…

Ubuntu提示“没有根文件系统 ”

安装Ubuntu时,提示“没有定义根文件系统” 原因:分区错误 解决: 将Ubuntu分区,删除重新创建分区,注意挂载点为“/” 此时就可以单击“继续”,进行下一步安装 以上分区方式,是没有交换分区的,下面提供两种带交换分区的分区方式: 第一种,包含4个分区,分别为 /分区(…

安装linux显示没有定义根文件系统,XP用Wubi安装Ubuntu提示“没有定义根文件系统,请返回分区菜单...

在安装Ubuntu时&#xff0c;到自定义分区一步时&#xff0c;会出现“没有根文件系统”&#xff0c;这时千万别将硬盘分区表重建&#xff0c;那样会让硬盘到数据都格式化到&#xff0c;你只需在ext4 或者 ext3 分区项上双击&#xff0c;加上挂载点为“\”就可以了。 有图有真相&…

在虚拟机安装中遇到的问题

问题一&#xff1a;在命令行模式下输入ifconfig时&#xff0c;显示系统不能识别这个命令&#xff0c;需要安装&#xff0c;输入sudo apt install net-tools。 问题二&#xff1a;sudo命令输入密码时&#xff0c;光标不移动&#xff0c;只要输入正确密码&#xff0c;回车就可以。…

安装Ubuntu时,提示“没有根文件系统 ”

安装Ubuntu时&#xff0c;提示“没有定义根文件系统” 原因&#xff1a;分区错误 解决&#xff1a; 将Ubuntu分区&#xff0c;删除重新创建分区&#xff0c;注意挂载点为“/” 此时就可以单击“继续”&#xff0c;进行下一步安装 以上分区方式&#xff0c;是没有交换分区的&a…

synchronized,voliate-详解

一.synchronized底层原理: synchronized关键字&#xff0c;在底层编译后的jvm指令中&#xff0c;会有monitorenter(枷锁)和monitorexit(释放锁)两个指令. monitorenter指令执行的时候会干什么呢? 每个对象都有一个关联的monitor&#xff0c;比如一个对象实例就有一个monitor…

volatile的作用及原理

前言 voliate关键字的两个作用&#xff1a; 1、 保证变量的可见性&#xff1a;当一个被volatile关键字修饰的变量被一个线程修改的时候&#xff0c;其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候&#xff0c;虚拟机会强制它被值刷…

voliate和synchronized

线程安全考虑三个方面&#xff1a;原子性&#xff0c;可见性&#xff0c;有序性 为什么使用voliate关键字&#xff1f; 正常情况下编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存.而在这个过程,变量的新值对其他线程是不可…