3. 根文件系统基础:概念、BusyBox原理与Tina组成

说到根文件系统,很多刚接触嵌入式Linux的朋友容易把它跟内核搞混。我早期带项目时就遇到过一位同事,拿着编译好的内核镜像反复烧写,系统就是起不来——最后发现根文件系统压根没放进去。嗯,这种坑踩过一次就记住了。

说白了,根文件系统就是Linux启动后挂载的第一个文件系统,它提供了系统运行所需的目录结构、基础命令、库文件和配置文件。内核负责驱动硬件,根文件系统负责提供用户态的一切。

3.1 根文件系统的核心概念

根文件系统以/为起点,包含几个关键目录。我整理了一张结构图,帮你快速建立整体认知:

根文件系统核心结构 / (根) /bin /sbin /etc /lib /dev /tmp 各目录作用: • /bin — 用户级基础命令(ls, cp, mv, sh 等),系统启动和修复必需 • /sbin — 系统管理命令(ifconfig, fdisk, init 等),通常只有 root 使用 • /etc — 配置文件集中地(inittab, fstab, passwd, init.d/ 等) • /lib — 共享库和内核模块(ld-linux, libc, 驱动 .ko 文件) • /dev — 设备节点(console, ttyS0, mmcblk0 等),静态或 devtmpfs 自动创建 • /tmp — 临时文件,通常挂载为 tmpfs,掉电即失 提示:Tina 还会额外添加 /usr, /var, /mnt, /overlay 等目录, 但上面这 6 个是根文件系统的"骨架",缺一个都可能起不来。

关键认知:根文件系统不是"一个文件",而是一套目录树结构。内核启动后挂载它,然后执行 /sbin/init 作为第一个用户态进程。如果 /sbin/init 找不到,系统会直接 panic。

3.2 BusyBox原理——一个程序包揽天下

嵌入式设备存储空间有限,不可能像PC一样装几百个独立命令。BusyBox就是来解决这个问题的。它把 ls、cp、mv、sh、ifconfig 等200多个常用命令塞进一个可执行文件里,通过符号链接或硬链接来区分调用哪个功能。

我举个例子你就明白了:

# 查看 BusyBox 支持的命令列表
/bin/busybox --list

# 手动调用某个命令
/bin/busybox ls -l /etc

# 符号链接方式
ln -s /bin/busybox /bin/ls
ln -s /bin/busybox /bin/cp
# 执行 /bin/ls 时,BusyBox 通过 argv[0] 判断要执行 ls 功能

BusyBox的编译配置非常灵活。你可以通过 make menuconfig 选择要开启哪些命令,每个命令还可以细调参数。我在Tina上做产品时,一般会关掉那些用不到的——比如设备里没有USB网卡,就把 ifconfig 的无线相关选项去掉,能省下几十KB。

特性 BusyBox GNU Coreutils
单个程序体积 ~300KB~1MB(视配置) 每个命令几十KB~几MB
命令数量 200+(可裁剪) 100+(固定)
依赖库 通常仅 libc 依赖较多
适用场景 嵌入式、initramfs 桌面/服务器
配置灵活性 极高(menuconfig) 低

我的习惯:在Tina中,我会先保持BusyBox默认配置编译一版,启动后跑 busybox --list 看看实际用到了哪些命令,然后回头裁剪。这样不会漏掉关键工具。

3.3 Tina中根文件系统的组成

Tina的根文件系统不是手写的,而是通过一套构建系统自动生成。你执行 make 后,它会经历几个阶段:

  1. 编译BusyBox — 生成 busybox 可执行文件及符号链接
  2. 安装库文件 — 从工具链的 sysroot 复制所需的 .so 到 /lib
  3. 生成配置文件 — 填充 /etc 下的 inittab、fstab、passwd 等
  4. 创建设备节点 — 静态创建 /dev/console、/dev/null 等关键节点
  5. 打包镜像 — 根据配置格式(squashfs、jffs2、ext4、initramfs)生成最终文件

我们来看看Tina根文件系统里几个关键目录的具体内容:

/bin — 用户命令

这里放的是系统启动和日常操作必需的命令。Tina默认包含:

  • sh — 默认shell(BusyBox的ash)
  • ls, cp, mv, rm, cat, echo — 文件操作基础
  • mount, umount — 挂载文件系统
  • ps, top, kill — 进程管理
  • sed, awk, grep — 文本处理

/sbin — 系统管理命令

这些命令通常只有root权限才能执行:

  • init — 1号进程,PID永远为1
  • ifconfig, route — 网络配置
  • fdisk, mkfs — 磁盘分区格式化
  • reboot, poweroff — 系统控制
  • logread — 查看syslog

/etc — 配置文件

这是根文件系统里最需要关注的地方。我调试驱动时,一半时间都在跟这里的文件打交道:

文件 作用 常见问题
inittab 定义init启动的进程和运行级别 漏写 ttyS0::respawn:/bin/sh 导致串口无shell
fstab 自动挂载表(mmc、nfs、tmpfs等) 挂载参数错误导致系统启动卡住
init.d/rcS 系统初始化脚本,按顺序执行 脚本里依赖顺序不对,驱动加载失败
passwd / shadow 用户账户信息 忘记设root密码,telnet连不上
resolv.conf DNS配置 域名解析失败,ping IP通但ping域名不通

我曾经踩过的坑:有一次调试全志V3s的网卡驱动,系统启动后eth0死活拿不到IP。查了两天,最后发现是 /etc/init.d/rcS 里网络初始化脚本跑在了驱动加载之前。驱动还没注册好,脚本就去 ifconfig eth0 up,当然失败。后来我在rcS里加了 sleep 1 等待驱动就绪,问题解决。

/lib — 库与模块

这个目录决定了你的程序能不能跑起来。Tina的 /lib 通常包含:

  • libc.so — C运行时库(glibc或musl,取决于工具链配置)
  • ld-linux.so — 动态链接器
  • libm.so, libpthread.so, librt.so — 数学、线程、实时库
  • modules/ — 内核模块存放目录,驱动 .ko 文件放这里
  • libgcc_s.so — GCC运行时支持库,某些程序必须

你可以用 readelf -d 查看一个程序依赖哪些库:

# 在Tina开发板上执行
readelf -d /bin/busybox | grep NEEDED
 0x00000001 (NEEDED)  Shared library: [libc.so]
 0x00000001 (NEEDED)  Shared library: [ld-linux.so]

BusyBox只依赖libc和链接器,这也是它体积小的原因之一。如果你的应用程序依赖更多库,记得检查 /lib 下有没有,没有的话要从工具链的sysroot里拷过来。

3.4 根文件系统的构建流程

在Tina中,根文件系统的构建是自动化的。但理解它的步骤,对你排查问题非常有帮助:

  1. 配置阶段 — make menuconfig 选择目标板、文件系统类型、软件包
  2. 编译阶段 — 编译内核、BusyBox、第三方库、应用程序
  3. 安装阶段 — 把所有产物安装到一个临时目录(out/<板型>/compile_dir/target/rootfs)
  4. 后处理阶段 — 设置文件权限、创建设备节点、生成 /etc 配置文件
  5. 打包阶段 — 根据配置生成 squashfs、jffs2、ext4 或 initramfs 镜像

调试技巧:如果你在Tina中改了某个配置文件(比如 inittab),不需要重新编译整个系统。直接修改 out/<板型>/compile_dir/target/rootfs/etc/inittab,然后重新打包镜像即可。我经常用这个方法来快速验证配置修改。

根文件系统是嵌入式Linux的基石。你想想看,内核再牛,没有根文件系统它也跑不了任何应用程序。理解它的组成和构建逻辑,后面做驱动调试、系统裁剪、启动优化才能得心应手。