3. RootFS基础概念
在嵌入式 Linux 系统中,内核启动完成后必须挂载一个根文件系统(RootFS),才能运行用户空间的第一个进程。RootFS 不仅是“存放文件的目录”,更是内核与用户程序之间的运行契约:内核通过它找到 /sbin/init,动态链接器通过它找到 /lib 下的共享库,应用程序通过它访问 /dev、/proc、/sys 等虚拟文件系统。理解 RootFS 的目录结构、FHS 标准、分区读写策略以及 initramfs 与 rootfs 的区别,是 BusyBox 裁剪与定制工程的基础。
一、根文件系统的目录结构与 FHS 标准
Linux 根文件系统遵循 FHS(Filesystem Hierarchy Standard,文件系统层次标准)。FHS 规定了各目录的用途,使不同发行版、不同架构的系统具有一致的布局。嵌入式系统虽然可以精简,但核心目录仍需保留,否则 BusyBox 的 applet 和动态链接器会找不到依赖。
| 目录 | 用途 | 嵌入式常见内容 |
|---|---|---|
/bin | 所有用户可用的基本命令 | BusyBox 软链接:ls、cp、sh |
/sbin | 系统管理命令 | init、ifconfig、reboot |
/etc | 系统配置文件 | inittab、fstab、passwd、profile |
/lib | 共享库与内核模块 | ld-uClibc.so、libc.so、modules/ |
/dev | 设备节点 | console、null、ttyS0(或由 devtmpfs 挂载) |
/proc | 内核与进程信息 | proc 虚拟文件系统挂载点 |
/sys | 设备与驱动模型 | sysfs 虚拟文件系统挂载点 |
/tmp | 临时文件 | 通常挂载 tmpfs,可写 |
/var | 可变数据 | 日志、运行时状态 |
/usr | 用户程序与只读数据 | 可进一步精简或合并到根 |
在 BusyBox 场景中,/bin 与 /sbin 通常只放一个 busybox 二进制,其余命令都是指向它的符号链接。例如:
lrwxrwxrwx 1 root root 7 Jan 1 00:00 /bin/ls -> busybox
lrwxrwxrwx 1 root root 7 Jan 1 00:00 /bin/cp -> busybox
lrwxrwxrwx 1 root root 7 Jan 1 00:00 /sbin/init -> ../bin/busybox
这种“单二进制 + 软链接”的结构极大节省了存储空间,但也要求 RootFS 的目录层级必须正确,否则 init 无法启动,或动态链接器找不到 /lib/ld-uClibc.so.0。
二、只读与可写分区:为什么嵌入式 RootFS 常设为只读
嵌入式设备通常使用 NAND、NOR、eMMC 或 SD 卡作为存储介质。这些介质有擦写寿命限制,突然断电时也容易损坏文件系统。因此,工程上常把 RootFS 设计为“只读根 + 可写数据分区”的组合。
- 只读根分区:存放
/bin、/sbin、/lib、/etc等系统文件,挂载为squashfs、cramfs或只读ext4。优点是抗断电、节省空间、防止误改。 - 可写数据分区:存放配置、日志、用户数据,挂载到
/data、/var或/tmp,使用jffs2、ubifs、ext4或tmpfs。 - 临时可写层:若必须修改
/etc,可用overlayfs把只读根与可写目录叠加,既保持根只读,又允许局部修改。
三、initramfs 与 rootfs 的区别
很多初学者会把 initramfs 和 rootfs 混为一谈。实际上,rootfs 是内核在启动时创建的一个内存文件系统实例,它始终存在;initramfs 则是被内核解压到 rootfs 中的一段 cpio 归档。换句话说,rootfs 是“容器”,initramfs 是“初始内容”。
当内核启动时,会先挂载 rootfs,然后尝试执行 /init。如果内核配置了 initramfs,这个 /init 就来自 initramfs 归档;如果 initramfs 中的 /init 负责加载驱动、挂载真正的磁盘根文件系统,并执行 switch_root,那么最终的根就切换到了磁盘上的 RootFS。若没有 initramfs,内核则直接挂载 root= 指定的设备作为根。
四、案例与误区复盘
假设我们为一个 ARM 开发板制作 RootFS,采用 BusyBox 1.36、uClibc 交叉编译。目录结构如下:
rootfs/
├── bin/ # busybox 及软链接
├── sbin/ # init 软链接
├── etc/
│ ├── inittab
│ ├── fstab
│ └── init.d/rcS
├── lib/
│ ├── ld-uClibc.so.0
│ └── libc.so.0
├── dev/ # 空目录,由 devtmpfs 挂载
├── proc/ # 空目录,由 proc 挂载
├── sys/ # 空目录,由 sysfs 挂载
├── tmp/ # 空目录,由 tmpfs 挂载
└── data/ # 可写分区挂载点
常见误区包括:
- 只复制 busybox 而忘记创建软链接:导致
ls、cp等命令不存在,系统启动后无法操作。 - 忘记
/dev/console:内核启动时无法打开控制台,串口没有输出,误以为内核挂了。 - 把
/etc放在只读分区却需要运行时修改:应使用 overlayfs 或把配置放到/data并在启动脚本中复制。 - 混淆 initramfs 与 rootfs:以为 initramfs 就是最终根,结果没有执行
switch_root,磁盘根文件系统从未被挂载。
root=、init= 和 console= 参数是否正确;再确认 /init 或 /sbin/init 是否存在且可执行;最后检查动态链接器路径是否与交叉编译工具链一致。总结来说,RootFS 是嵌入式 Linux 用户空间的基石。FHS 标准决定了目录布局,只读与可写分区策略决定了系统可靠性与寿命,initramfs 与 rootfs 的区别则决定了启动流程的设计方式。掌握这些概念后,才能进一步使用 BusyBox 裁剪命令、制作镜像并完成根文件系统定制。