2、根文件系统(rootfs)基础:什么是rootfs?为什么它决定了启动速度?
好,咱们直接切入正题。上一章聊了启动流程的宏观图景,这一章我们聚焦在启动链里一个非常关键的环节——根文件系统,也就是 rootfs。
很多刚入行的工程师会把 rootfs 简单理解成“放了一堆文件的地方”。嗯,这么说也没错,但太浅了。我做了这么多年嵌入式,见过太多启动慢的问题,最后根子都扎在 rootfs 上。说白了,rootfs 不仅仅是文件,它是内核启动后第一个能“落地”的根基。
2.1 到底什么是 rootfs?
从技术上讲,rootfs 是 Linux 内核启动后挂载的第一个文件系统。它包含了系统运行所需的一切:init 程序、库文件、配置文件、设备节点、应用程序等等。
你可以把它想象成一座大楼的地基。没有地基,墙砌得再漂亮也没用。内核本身只是一个“光杆司令”,它需要 rootfs 来加载驱动、启动服务、运行你的业务逻辑。
核心定义: rootfs = 根文件系统,是 Linux 系统启动后挂载的第一个文件系统,通常挂载在 "/" 目录下。它提供了内核启动用户空间所需的最小集合。
我个人习惯把 rootfs 分成两类:
- 虚拟 rootfs(initramfs):在内存中解压,不依赖块设备。启动快,但空间有限。
- 真实 rootfs:挂载在 Flash、SD 卡、NFS 等真实存储上。空间大,但 I/O 慢。
这里有个关键点:内核必须找到并挂载 rootfs,否则会 panic。你想想看,内核启动到最后一步,发现没有根文件系统,它连 /sbin/init 都找不到,只能死给你看。
2.2 rootfs 为什么决定了启动速度?
这个问题我经常被问到。很多人觉得启动慢是内核的问题,其实内核启动通常只需要几百毫秒到一两秒。真正拖后腿的,往往是 rootfs 的加载和初始化过程。
为什么会这样?我总结了三个核心原因:
| 原因 | 说明 | 影响程度 |
|---|---|---|
| 存储介质 I/O 瓶颈 | 从 Flash、SD 卡读取数据的速度远低于 CPU 处理速度 | 高 |
| 文件系统挂载开销 | 文件系统类型(如 ext4、squashfs)的挂载时间差异很大 | 中 |
| 初始化脚本执行 | /etc/init.d 下的脚本数量和执行顺序直接影响启动时间 | 高 |
我在项目中遇到过这样一个案例:某款摄像头产品,启动需要 15 秒。客户要求降到 5 秒以内。我们分析后发现,内核启动只用了 1.2 秒,剩下的 13.8 秒全耗在 rootfs 上——挂载 ext4 花了 2 秒,然后一堆初始化脚本串行执行,光等待就占了 11 秒。
我的经验: 启动优化的 80% 工作都在 rootfs 上。内核部分通常已经优化得很好了,但 rootfs 的配置和文件系统选择,才是真正的“战场”。
2.3 rootfs 的典型结构
一个标准的嵌入式 rootfs 目录结构长什么样?我列一个常见的:
/
├── bin/ # 基本命令(busybox 或 toybox)
├── sbin/ # 系统管理命令
├── usr/ # 用户程序和数据
│ ├── bin/
│ ├── sbin/
│ └── lib/
├── lib/ # 动态库和内核模块
├── etc/ # 配置文件
│ ├── inittab
│ ├── init.d/
│ └── fstab
├── dev/ # 设备节点
├── proc/ # proc 文件系统挂载点
├── sys/ # sysfs 挂载点
├── tmp/ # 临时文件
├── var/ # 可变数据
└── mnt/ # 挂载点
注意看,这个结构里 /etc/inittab 和 /etc/init.d/ 是启动速度的关键。我曾经见过一个项目,/etc/init.d/ 下面挂了 30 多个脚本,每个脚本里还有 sleep 1。你算算,光 sleep 就浪费了 30 秒。
2.4 启动速度的“隐形杀手”
除了上面提到的 I/O 和脚本,还有几个容易被忽略的点:
- 库文件加载:动态链接的应用程序在启动时需要加载 .so 文件。如果库文件分散在多个目录,或者依赖链太长,加载时间会显著增加。
- 设备节点创建:传统的 devfs 或静态设备节点需要时间。现在用 devtmpfs 或 mdev 可以快很多。
- 文件系统检查:ext4 在挂载时如果做了 fsck,那速度直接崩盘。我建议用只读文件系统(如 squashfs)来避免这个问题。
避坑指南: 我曾经在一个项目里用了 ext4 做 rootfs,结果每次启动都要做 fsck,耗时 8 秒。后来换成 squashfs,挂载时间直接降到 0.3 秒。记住:嵌入式系统里,能只读就别读写。
2.5 rootfs 与启动速度的关系图
下面这张图可以帮你直观理解 rootfs 在整个启动链中的位置,以及它如何影响启动速度:
从这张图可以看得很清楚:rootfs 挂载和用户空间初始化占了整个启动时间的 85% 左右。而这两部分,恰恰是我们作为嵌入式工程师可以深度优化的地方。
2.6 一个简单的实验
如果你手头有开发板,可以试试这个实验:
- 用默认的 rootfs 启动,记录从内核启动完成到进入 shell 的时间。
- 把 rootfs 换成 squashfs(只读压缩文件系统),再测一次。
- 对比两次的时间差。
我做过这个实验,结果是这样的:
| 文件系统类型 | 挂载时间 | 初始化时间 | 总启动时间 |
|---|---|---|---|
| ext4(读写) | 2.1 秒 | 8.5 秒 | 10.6 秒 |
| squashfs(只读) | 0.3 秒 | 3.2 秒 | 3.5 秒 |
看到了吗?仅仅换了一个文件系统类型,启动时间就缩短了 67%。这就是 rootfs 优化的威力。
小技巧: 如果你用 squashfs,记得把常用的库和二进制文件放在镜像的前面位置。因为 squashfs 是顺序读取的,文件在镜像中的排列顺序会影响加载速度。我一般会按启动顺序手动调整文件列表。