3. 根文件系统基础

在嵌入式 Linux 系统中,内核启动完成后会挂载一个根文件系统(rootfs),并执行第一个用户态程序。根文件系统不仅是存放程序和配置的“仓库”,更是内核与用户空间之间的桥梁。理解它的目录结构、FHS 标准以及必备文件与库,是使用 Buildroot 构建系统的前提。

一、根文件系统的核心作用

根文件系统是挂载在 / 目录下的文件系统,它至少需要提供以下能力:

  • 提供 init 进程(或 systemd、busybox init),完成系统初始化;
  • 提供 shell、常用命令和动态链接库,让用户能够交互;
  • 提供设备节点(/dev)、挂载点(/mnt、/media)和运行时目录(/proc、/sys、/tmp);
  • 提供配置文件(/etc)和用户程序(/bin、/sbin、/usr)。

如果根文件系统缺失或损坏,内核会打印 Kernel panic - not syncing: No init found 并停止启动。因此,根文件系统与内核、Bootloader 并列为嵌入式 Linux 的三大基础组件。

二、FHS 标准与目录职责

FHS(Filesystem Hierarchy Standard,文件系统层次标准)定义了 Linux 目录的命名和用途。Buildroot 默认生成的根文件系统遵循 FHS 规范,主要目录职责如下表所示。

目录 用途 Buildroot 中的典型内容
/bin 所有用户可用的基础命令 busybox 软链接:ls、cp、sh
/sbin 系统管理命令 init、ifconfig、reboot
/etc 系统配置文件 inittab、fstab、passwd、profile
/lib 启动和运行所需共享库 ld-linux.so、libc.so、内核模块
/usr 用户程序与只读数据 /usr/bin、/usr/lib、/usr/share
/dev 设备节点 console、null、ttyS0
/proc 内核与进程信息(虚拟) 由 proc 文件系统挂载生成
/sys 设备与驱动模型(虚拟) 由 sysfs 挂载生成
/tmp 临时文件 通常挂载为 tmpfs
/var 可变数据 日志、锁文件、运行时状态

需要特别注意的是,/proc、/sys、/dev 在 Buildroot 输出目录中可能只是空目录,真正的节点和内容由内核在启动时挂载虚拟文件系统生成。因此,构建时看到空目录并不代表出错。

三、必备文件与库

一个可启动的最小根文件系统,通常包含以下关键文件:

  1. init 程序:内核启动后执行的第一个用户态进程,PID 为 1。Buildroot 默认使用 BusyBox 的 init,读取 /etc/inittab。
  2. 动态链接器与 C 库:如 /lib/ld-linux-armhf.so.3、/lib/libc.so.6。若使用 glibc 或 uClibc,必须保证库文件与目标架构匹配。
  3. 设备节点:至少需要 /dev/console 和 /dev/null,否则 init 无法输出日志。
  4. 配置文件:/etc/inittab 定义启动流程,/etc/fstab 定义挂载点,/etc/passwd 定义用户。
  5. Shell 与基础命令:通常由 BusyBox 提供,通过软链接指向 /bin/busybox。

Buildroot 输出目录 output/target/ 就是根文件系统的原始骨架。执行 make 后,可以在其中看到 bin/、etc/、lib/、usr/ 等目录。最终镜像会将其打包为 ext4、ubifs 或 cpio 格式。

四、目录结构与依赖关系图解

下图展示了根文件系统中各目录的层级关系,以及启动时最关键的依赖路径:内核 → init → 动态库 → BusyBox → 配置文件。

根文件系统目录结构与启动依赖 / (rootfs) /bin /sbin /etc /lib /usr /dev busybox (ls/cp/sh 软链接) init (PID 1) ld-linux.so libc.so.6 inittab fstab / passwd 启动依赖链: 内核挂载 rootfs 执行 /sbin/init 加载动态库 读取 inittab 启动 shell/服务

五、典型场景:Buildroot 输出目录分析

在 Buildroot 工程中执行编译后,可以进入输出目录查看根文件系统骨架:

cd output/target
ls -la
# 输出示例:
# drwxr-xr-x  bin
# drwxr-xr-x  dev
# drwxr-xr-x  etc
# drwxr-xr-x  lib
# drwxr-xr-x  proc
# drwxr-xr-x  sbin
# drwxr-xr-x  sys
# drwxr-xr-x  tmp
# drwxr-xr-x  usr
# drwxr-xr-x  var

ls -l bin/busybox
# -rwxr-xr-x 1 root root  ... bin/busybox

ls -l bin/ls
# lrwxrwxrwx 1 root root 7 ... bin/ls -> busybox

可以看到,BusyBox 通过软链接提供大量命令,极大减小了根文件系统体积。这也是嵌入式系统常用的“单一二进制 + 软链接”策略。

六、常见误区与复盘

误区一:只复制可执行文件,忘记动态库。 如果目标程序依赖 libc.so.6 或 ld-linux.so,但 /lib 中没有对应文件,运行时会报 No such file or directory,即使文件确实存在。使用 readelf -d 或 ldd 可以查看依赖。

误区二:忽略 /dev/console。 内核启动 init 时,若没有控制台设备节点,日志无法输出,系统看起来“卡死”。Buildroot 默认会通过 devtmpfs 或静态节点解决,但手工制作 rootfs 时必须显式创建。

复盘建议: 构建完根文件系统后,先用 QEMU 或目标板启动一次,确认能进入 shell;再检查 /lib 下动态库是否齐全、/etc/inittab 是否存在、/dev/console 是否可访问。这三项是根文件系统能否正常启动的关键检查点。

掌握根文件系统的目录结构、FHS 标准以及必备文件与库之后,就能理解 Buildroot 在 output/target/ 中生成的内容,并为后续配置 BusyBox、添加自定义文件和裁剪系统体积打下基础。