图文详解Linux根文件系统

大家好,这里是物联网心球。

本文的主题是Linux根文件系统,在进入正文之前,我们先来思考一个问题:什么是Linux根文件系统?

根文件系统被认为是Linux内核启动后挂载的第一个文件系统,是Linux文件树的根,是所有绝对路径的起点。笔者认为,这种表达并不够精准。接下来,我们从内核视角出发,深入了解Linux根文件系统。

注意:本文基于内核6.10版本讲解。

1.根文件系统从何而来?

Linux用户接触到的根文件系统只是挂载在根目录("/")下的一个真实的文件系统 ,我们习惯把它称为真实根文件系统。内核初始化时,会挂载一个rootfs文件系统(伪文件系统,只存在内存中),rootfs是Linux内核启动后第一个挂载的文件系统,所以它才是Linux的根文件系统。

从rootfs到真实根文件系统需要经历三个环节:

  • 挂载rootfs伪文件系统。
  • 解析initramfs至rootfs。
  • 挂载真实根文件系统(如ext4)。

图1 真实根文件系统挂载流程

真实根文件系统挂载流程如图1所示。

首先,我们来看rootfs伪文件系统的挂载流程,rootfs文件系统定义如下:

复制代码
struct file_system_type rootfs_fs_type = {
    .name = "rootfs",
    .init_fs_context = rootfs_init_fs_context,
    .kill_sb = kill_litter_super,
};

我们展开rootfs_init_fs_context函数:

复制代码
static int rootfs_init_fs_context(struct fs_context *fc)
{
    if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs)
        return shmem_init_fs_context(fc);

    return ramfs_init_fs_context(fc);
}

发现rootfs实际上是一个**tmpfs** 或**ramfs文件系统** 实例。ramfstmpfs都是 Linux系统中基于内存的文件系统。它们将数据直接存储在RAM(物理内存)中,因此读写速度极快,但数据在系统重启后会丢失。

内核启动过程会调用start_kernel函数,该函数会执行一些列初始化工作,rootfs的初始化路径为:start_kernel()->vfs_caches_init()->mnt_init()->init_mount_tree()。init_mount_tree函数将会创建rootfs挂载实例和rootfs根目录,并将这些信息记录在内核。文件系统挂载过程这里不过多展开,对文件系统挂载感兴趣的同学可以阅读这篇文章:从ext4文件系统到Linux文件树

rootfs解决的是Linux文件树从0到1的问题,rootfs的根目录就是Linux文件树的根,后续的真实根文件系统需要挂载在rootfs的根目录。

有了根文件系统后,接下来,内核会解析initramfs至rootfs。initramfs是Linux启动过程中使用的临时文件系统,在真实根文件系统挂载前提供必要的驱动和工具。initramfs经常被当做Linux文件系统,其实它并不是真正的文件系统,内核中并没有定义该类型的文件系统。initramfs本质是一个cpio归档文件,包含了挂载真实根文件系统需要用到的所有文件。内核会对initramfs进行解析,并将解析出来的文件一个个保存在rootfs文件系统中(注意此处并不是挂载,可以理解为文件拷贝)。initramfs解析对应的初始化路径为:start_kernel()->arch_call_rest_init()->rest_init()->kernel_init()->......->do_populate_rootfs()。

initramfs解析完毕后,rootfs根目录下会有一个init脚本,init脚本将会完成真实文件系统挂载,并将系统运行环境也切换至真实文件系统。对应的初始化路径为:start_kernel()->vfs_caches_init()->rest_init()->kernel_init()->run_init_process("/init")。run_init_process函数将会执行init脚本。

2.initramfs详解

initramfs并不是真正的文件系统,它是压缩(如lz4、gzip、zstd等压缩格式)了的cpio归档文件,cpio(Copy In and Out)是一种在Unix和Linux系统中广泛使用的归档工具,用于将多个文件和目录打包成一个单独的归档文件,同时保留文件的元数据(如权限、所有者、时间戳等)。

cpio支持多种归档格式:bin、odc、newc、crc、tar等。initramfs采用的是newc格式,如图2所示。

图2 initramfs格式

initramfs由一条条文件记录构成,每条文件记录格式为:

  • 文件头(110字节)
  • 文件名
  • 0-3字节填充
  • 文件数据
  • 0-3字节填充

文件头包含文件的元数据,固定大小为110字节,其格式见表1。

表1 newc文件头

为了加深大家对newc格式的理解,我们创建一个最小initramfs并打包成newc格式,测试脚本如下:

bash 复制代码
#!/bin/bash

#创建文件树
mkdir bin conf etc lib
touch etc/test.txt
#创建init脚本
echo "#!/bin/sh" > init
#打包为newc格式
find bin conf etc lib init -depth | cpio -o -H newc > initramfs.cpio

执行测试脚本后将生成一个initramfs.cpio归档文件,执行以下命令验证文件清单:

复制代码
cpio -t < demo.cpio

文件清单如下:

复制代码
# cpio -t < initramfs.cpio
bin
conf
etc/test.txt
etc
lib
init

执行hexdump -C initramfs.cpio查看归档文件记录,输出结果如下: 最终cpio中的文件记录将一条条被解析,并存储在rootfs文件系统中。

3./init脚本

/init脚本是initramfs的核心,它将完成以下关键任务:

  • 挂载/proc, /sys, /dev等虚拟文件系统,并创建必要的设备节点。
  • 加载访问存储设备和文件系统所需的内核模块。
  • 挂载真实根文件系统。
  • 将系统根目录切换到真实根文件系统,并启动/sbin/init进程(1号进程)。

实际的/init脚本一般都比较复杂,为了便于讲解,我们只关注根文件系统挂载相关的内容,如图3所示。

图3 /init脚本工作原理

内核调用run_init_process("/init")函数将会执行init脚本,run_init_process函数主要任务是执行用户空间的第一个进程,从而完成从内核态到用户态的切换。

真实根文件系统(如ext4)未挂载之前,系统执行文件相关的操作都是在rootfs文件系统中进行。/init脚本首先会挂载伪文件系统(proc、sysfs、devtmpfs等)至rootfs。接着,内核会读取块设备,并挂载块设备中的真实根文件系统挂载至rootfs(挂载点由用户自行定义)。最后,将已挂载的伪文件系统移动至真实根文件系统,以及执行initramfs中的switch_root命令将运行环境切换至真实根文件系统。

switch_root 命令的核心功能包括:

  • 将新根目录设置为系统的根文件系统。
  • 执行新根文件系统中的init程序(通常是 /sbin/init)。
  • 清理并释放initramfs占用的内存空间。

switch_root语法格式如下:

复制代码
switch_root [-c /dev/console] NEW_ROOT NEW_INIT [ARGUMENTS_TO_INIT]
  • NEW_ROOT:已经挂载好的真正根文件系统挂载点,例如 /newroot。

  • NEW_INIT:真正根文件系统中的init程序路径,通常是/sbin/init(软链接,指向init或systemd)。

  • -c:可选,重定向新系统的控制台设备。

总结:

真实根文件系统启动流程分为三步:首先,挂载rootfs文件系统,创建根目录,解决文件系统从0到1的问题;接着,解析initramfs并将initramfs中的文件保存至rootfs文件系统;最后,执行initramfs中的init脚本,init脚本将完成真实根文件系统挂载。

相关推荐
一个平凡而乐于分享的小比特2 天前
嵌入式根文件系统详解与对比
jffs2·yaffs2·initramfs·cramfs·squashfs·romfs
崎岖Qiu3 天前
【OS笔记35】:文件系统的使用、实现与管理
笔记·操作系统·存储管理·文件系统·os
崎岖Qiu4 天前
【OS笔记36】:文件存储空间管理(一)- 空闲区表法
笔记·操作系统·存储管理·文件系统·os
故事不长丨4 天前
深度解析C#文件系统I/O操作:File类与FileInfo类的核心用法与场景对比
c#·文件系统·file·fileinfo·i/o操作·i/o流
一个平凡而乐于分享的小比特5 天前
CPU上电启动到程序运行全流程详解
linux·uboot·根文件系统·cpu上电到启动
程序猿编码9 天前
隐形的内核后门:无模块Linux内核植入技术详解
linux·c语言·内核·linux内核·内核植入
HHONGQI12311 天前
嵌入式系统应用——FLASH应用之文件系统
文件系统
大模型铲屎官13 天前
【操作系统-Day 46】文件系统核心探秘:深入理解连续分配与链式分配的实现与优劣
人工智能·python·深度学习·大模型·操作系统·文件系统·计算机组成原理
一个平凡而乐于分享的小比特15 天前
核心原理:文件系统 vs 归档格式
linux·文件系统·归档格式
程序猿编码19 天前
手动清理 TCP TIME-WAIT 套接字:Linux 内核模块的实现与原理
linux·网络·tcp/ip·linux内核·套接字