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

article/2025/9/19 19:46:14

挂载根文件系统之rootfs

文章目录

        • 挂载根文件系统之rootfs
        • 一、开篇
        • 二、rootfs根文件系统
          • (2-1)初始化rootfs
          • (2-2)挂载rootfs文件系统
          • (2-3)创建简单的rootfs根文件系统目录和文件
          • (2-4)打开0、1、2文件描述符
        • 三、挂载用户指定的根文件系统
        • 四、结尾

一、开篇

​ 对于linux内核,文件系统可以说是给内核增添了无尽的“乐趣”。在linux运行情况下,对于一个文件系统来说,只有挂载到内存中目录树的一个目录下,文件系统才会被linux所访问。linux内核中很多地方都运用“父—子”概念。在文件系统部分,也同样使用了该概念。对于linux内核中第一个文件系统,不能通过mount命令或者系统调用来挂载。这时候内核是通过以下两种机制来挂载根文件系统。

​ 根文件系统的概念这里从内核的角度和用户的角度来看,先从linux内核角度来看,根文件系统是rootfs;从用户的角度来看,根文件系统是用户指定的根文件系统,在linux引导时通过内核参数root=指定。二者的关系是:在linux内核启动流程的后续会把用户指定的根文件系统挂载到rootfs文件系统的根目录下。

二、rootfs根文件系统

​ 在linux内核启动过程中,最先挂载的根文件系统是rootfs文件系统,该文件系统是一个内存文件系统,即是基于内存的,而且对用户隐藏。该文件系统非常重要,每个进程所使用的标准输入、标准输出和标准错误,对应文件描述符0、1和2,这3个文件描述符都对应rootfs文件系统中的字符设备文件"/dev/console"。下面将从源码的角度来看看rootfs

(2-1)初始化rootfs

初始化rootfs文件系统由init_rootfs()函数完成,定义如下:

int __init init_rootfs(void)
{int err = register_filesystem(&rootfs_fs_type);if (err)return err;if (IS_ENABLED(CONFIG_TMPFS) && !saved_root_name[0] &&(!root_fs_names || strstr(root_fs_names, "tmpfs"))) {err = shmem_init();is_tmpfs = true;} else {err = init_ramfs_fs();}if (err)unregister_filesystem(&rootfs_fs_type);return err;
}

在以上代码中,会调用register_filesystem()向linux内核注册rootfs文件系统类型rootfs_fs_type。定义如下:

static struct dentry *rootfs_mount(struct file_system_type *fs_type,int flags, const char *dev_name, void *data)
{static unsigned long once;void *fill = ramfs_fill_super;if (test_and_set_bit(0, &once))return ERR_PTR(-ENODEV);if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs)fill = shmem_fill_super;return mount_nodev(fs_type, flags, data, fill);
}static struct file_system_type rootfs_fs_type = {.name		= "rootfs",.mount		= rootfs_mount,.kill_sb	= kill_litter_super,
};
(2-2)挂载rootfs文件系统

挂载rootfs文件系统由init_mount_tree()函数完成,从该函数名可形象的知道该函数的功能是:初始化挂载树。函数定义如下:

static void __init init_mount_tree(void)
{struct vfsmount *mnt;struct mnt_namespace *ns;struct path root;struct file_system_type *type;type = get_fs_type("rootfs");if (!type)panic("Can't find rootfs type");mnt = vfs_kern_mount(type, 0, "rootfs", NULL);put_filesystem(type);if (IS_ERR(mnt))panic("Can't create rootfs");ns = create_mnt_ns(mnt);if (IS_ERR(ns))panic("Can't allocate initial namespace");init_task.nsproxy->mnt_ns = ns;get_mnt_ns(ns);root.mnt = mnt;root.dentry = mnt->mnt_root;mnt->mnt_flags |= MNT_LOCKED;set_fs_pwd(current->fs, &root);set_fs_root(current->fs, &root);
}

上述第11行代码,使用vfs_kern_mount()挂载rootfs文件系统。

第16行代码,使用create_mnt_ns(mnt)函数创建第一个挂载命令空间。

上述第20行代码,将设置0号线程的挂载命名空间。

第27和28行代码,将0号线程的当前工作目录和根目录设置为rootfs文件系统的根目录。

(2-3)创建简单的rootfs根文件系统目录和文件

在内核后续的启动过程中,default_rootfs()函数会在rootfs文件系统中创建必须的目录和文件节点。如下代码所示(/init/noinitramfs.c):

static int __init default_rootfs(void)
{int err;err = ksys_mkdir((const char __user __force *) "/dev", 0755);if (err < 0)goto out;err = ksys_mknod((const char __user __force *) "/dev/console",S_IFCHR | S_IRUSR | S_IWUSR,new_encode_dev(MKDEV(5, 1)));if (err < 0)goto out;err = ksys_mkdir((const char __user __force *) "/root", 0700);if (err < 0)goto out;return 0;out:printk(KERN_WARNING "Failed to create a rootfs\n");return err;
}
rootfs_initcall(default_rootfs);

从以上代码可知,default_rootfs()将创建/dev目录;创建控制台的字符设备文件/dev/console,主设备号是5,从设备号是1.

还将创建/root目录。最后会使用rootfs_initcall宏将default_rootfs函数加入到初始化节段中。

【特别注意】对于default_rootfs函数的编译将在没有设置initrd/initramfs的条件下进行。如果设置了【Initial RAM filesystem and RAMdisk(initramfs/initrd)support】选项。那么default_rootfs()函数将不会编译进linux内核!

在这里插入图片描述

(2-4)打开0、1、2文件描述符

在linux内核1号线程的线程函数中,会调用kernel_init_freeable()函数,在该函数中会打开控制台的字符设备文件/dev/console,得到文件描述符0,然后两次复制文件描述符0,得到文件描述符1和2。如下代码片段:

	/* Open the /dev/console on the rootfs, this should never fail */if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)pr_err("Warning: unable to open an initial console.\n");(void) ksys_dup(0);(void) ksys_dup(0);

​ linux内核的1号线程会使用try_to_run_init_process()试图加载用户空间的程序,从而使内核向用户空间转换,在这个过程中,1号线程将作为其他线程的父进程,从而子线程将继承打开的文件表,从而也将继承文件描述符0、1、2,这就是每个进程所使用的标准输入、标准输出和标准错误具有统一性的原因。

三、挂载用户指定的根文件系统

​ 在引导linux内核时,可以使用内核参数root来指定存储设备的名称,使用rootfstype内核参数指定根文件系统的类型,从而挂载用户的根文件系统。在源码中,具体的实现由prepare_namespace()函数来完成,这部分内容见该篇文章:《【linux kernel】linux内核如何挂载根文件系统》

四、结尾

该篇文章内容的源码出自linux内核版本:4.19.4,小生对比了linux内核2.6版本的源码,发现init_rootfs()init_mount_tree()这两个函数内容没有太大变化,可见linux内核设计的深厚功力啦!!下面附上这两个函数的定义:


2.6版本的linux内核(/fs/ramfs/inode.c)init_rootfs函数定义:

int __init init_rootfs(void)
{int err;err = bdi_init(&ramfs_backing_dev_info);if (err)return err;err = register_filesystem(&rootfs_fs_type);if (err)bdi_destroy(&ramfs_backing_dev_info);return err;
}

2.6版本的linux内核(/fs/namespace.c)init_mount_tree函数定义:

static void __init init_mount_tree(void)
{struct vfsmount *mnt;struct mnt_namespace *ns;struct path root;mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);if (IS_ERR(mnt))panic("Can't create rootfs");ns = create_mnt_ns(mnt);if (IS_ERR(ns))panic("Can't allocate initial namespace");init_task.nsproxy->mnt_ns = ns;get_mnt_ns(ns);root.mnt = ns->root;root.dentry = ns->root->mnt_root;set_fs_pwd(current->fs, &root);set_fs_root(current->fs, &root);
}

小生由于知识和精力有限,如若文章存在有不妥的地方,欢迎批评指正或与我讨论,哈哈!!


搜索关注【嵌入式小生】wx公众号获取更多精彩内容>>>>
请添加图片描述


http://chatgpt.dhexx.cn/article/4JFRI8El.shtml

相关文章

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缓存上进行,最后才写入内存.而在这个过程,变量的新值对其他线程是不可…

关于voliate关键字

现象 private static boolean is false;public static void main(String[] args) {new Thread(new Runnable() {Overridepublic void run() {System.out.println("thread 1 start");while (!is) {}System.out.println("thread 1 end");}}).start();try {T…

并发专题之---Voliate引发的各种原理问题

文章目录 前言JMMvoliate不保证原子性不保证原子性的解释AtomicInteger解决不保证原子性的问题为什么AtomicInteger可以解决原子性问题?CASCAS的内部原理CAS的缺点ABA问题原子引用解决ABA问题 禁止指令重排多线程环境下单例模式出现的问题双端检索机制解决办法双端检索机制的隐…

voliate类型的原理及用法

voliate类型&#xff0c;机器才读取执行代码读取内存后&#xff0c;将读取的内容存到高速缓冲区里&#xff0c;在硬件里是寄存器&#xff0c;这样在一下次读取的时候就可以直接从高速缓存区里面读取&#xff08;cache&#xff09;,这也是读取速度加快的原因&#xff0c;但是如果…

voliate(轻量级的同步机制)

voliate特点: JMM内存模型三大特性&#xff1a;可见性&#xff0c;原子性&#xff0c;有序性。 1.禁止指令重排序&#xff1a; voliate实现禁止指令重排&#xff0c;避免多线程环境下程序出现乱序执行的现象。 多线程环境中线程交替执行&#xff0c;由于编译器优化重排的…

voliate工作实际应用场景

哈喽大家好&#xff0c;我是IT老哥&#xff0c;今天我们来讲讲面试必问的voliate 单线程的情况下呢&#xff0c;我们肯定用不到这个voliate 只有在多线程的情景下才能用到&#xff0c;文章结尾我会举一个经典的案例 voliate三特性 保证可见性&#xff1b;不保证复合操作的原子性…