3. Linux文件系统层次标准

在嵌入式Linux根文件系统构建中,目录结构不是随意摆放文件的“杂物间”,而是遵循一套被广泛接受的约定——FHS(Filesystem Hierarchy Standard,文件系统层次标准)。FHS定义了Linux系统中目录的层级、命名和用途,使应用程序、库、配置和运行时数据能够被系统与开发者以可预期的方式定位。对于嵌入式系统而言,理解FHS不仅是“知道目录长什么样”,更是判断哪些目录必须存在、哪些可以裁剪、哪些需要根据存储介质和启动方式调整的基础。

一、FHS的核心思想与顶层目录

FHS的核心思想可以概括为三点:按用途分类、按可变性区分、按共享性划分。它把文件系统划分为若干顶层目录,每个目录承担明确职责。例如,/bin、/sbin、/lib存放系统启动和基本运行所需的关键程序与库;/etc存放主机专属配置;/usr存放用户态程序与只读数据;/var存放运行中会变化的数据;/dev、/proc、/sys则是设备节点和内核暴露的虚拟文件系统。

FHS还区分了“根文件系统”与“可独立挂载的文件系统”。在桌面发行版中,/usr、/var、/home常被单独分区;在嵌入式系统中,由于Flash容量有限,这些目录往往被合并到同一个只读或可读写分区中,甚至通过符号链接重新组织。

FHS 顶层目录结构与嵌入式关注点 / 根目录 /bin 基础命令 /sbin 系统管理命令 /lib 共享库与模块 /etc 配置文件 /usr 用户程序与数据 /var 可变运行数据 /dev 设备节点 /proc 进程与内核信息 /sys 设备与驱动模型 /tmp 临时文件 /home 用户主目录 /root root用户目录 嵌入式场景下的典型调整 • /bin、/sbin、/lib 可合并为 /usr/bin、/usr/sbin、/usr/lib,并用符号链接保持兼容 • /usr 常设为只读,减少Flash写磨损;/var、/tmp 挂载到 tmpfs 或可读写分区 • /dev 由 devtmpfs 动态生成,/proc、/sys 由内核挂载,无需在镜像中预置大量文件 • /etc 只保留目标板必需的配置,避免携带开发主机专属脚本和冗余服务配置

二、各目录用途与嵌入式裁剪原则

在嵌入式根文件系统中,裁剪不是简单删除目录,而是围绕“启动必需、运行必需、调试可选”三个层次做取舍。下面这张表列出了常见目录的用途、是否可裁剪以及典型处理方式。

目录 主要用途 嵌入式裁剪建议
/bin 启动和单用户模式所需的基础命令 保留 sh、ls、cp、mount 等;其余可合并到 /usr/bin
/sbin 系统管理、网络配置、文件系统检查 按需保留 ifconfig、route、fsck;调试工具可放入只读分区
/lib 动态链接库、内核模块 只保留应用程序实际依赖的 .so;内核模块按硬件裁剪
/etc 系统与应用程序配置 保留 fstab、inittab、网络配置;删除开发主机专属配置
/usr 用户态程序、库、共享数据 可设为只读;合并 /bin、/sbin、/lib 内容
/var 日志、锁文件、运行时状态 挂载到 tmpfs 或可读写分区;限制日志大小
/dev 设备节点 由 devtmpfs 自动生成,镜像中只需保留必要静态节点
/proc、/sys 内核与驱动暴露的虚拟文件系统 启动脚本中挂载,不占用镜像空间

三、方法流程:从FHS到可启动根文件系统

构建一个符合FHS精神、又适合嵌入式约束的根文件系统,通常遵循以下流程:

  1. 确定启动链需求:内核启动后需要执行哪个init程序?它依赖哪些库和配置文件?
  2. 建立最小目录骨架:创建 /bin、/sbin、/lib、/etc、/dev、/proc、/sys、/tmp、/var、/usr等目录。
  3. 填充必需文件:复制BusyBox或Coreutils、C库、内核模块、启动脚本和关键配置。
  4. 建立符号链接与合并策略:根据空间和只读需求,决定是否将 /bin、/sbin、/lib链接到 /usr下。
  5. 设置挂载点与权限:为 /proc、/sys、/tmp、/var准备挂载脚本,确保运行时目录可写。
  6. 验证与裁剪:通过启动日志和 ldd、strace检查缺失依赖,逐步删除未使用文件。

例如,在启动脚本中常见如下挂载操作:

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
mount -t tmpfs none /tmp
mount -t tmpfs none /var

这些虚拟文件系统不占用Flash空间,却为系统提供了标准目录语义。若缺少 /proc 或 /sys,许多程序将无法获取系统信息,甚至启动失败。

四、案例与误区复盘

某工业网关项目使用 64MB NAND Flash,最初直接复制桌面发行版的 /etc 和 /usr,导致镜像超过 120MB,且启动时因 /var/log 频繁写入而加速坏块产生。调整方案是:将 /usr 设为只读,/var 和 /tmp 挂载为 tmpfs,/etc 仅保留网络、串口和启动脚本,最终镜像压缩到 28MB,启动时间缩短约 40%。

常见误区:把FHS当成“必须原样保留所有目录”的硬性规定。实际上FHS允许通过符号链接和合并目录来适应只读、小容量场景。另一个误区是忽略 /var 的可写性,导致日志或PID文件写入失败,程序异常退出。

实践建议:在裁剪时先用 ldd 检查可执行文件的库依赖,再用 find 和 du 定位大文件;对 /etc 中的配置逐项确认是否被目标板实际读取。保持“启动必需优先、运行必需保留、调试工具按需”的原则,才能构建出既符合FHS精神又精简可靠的嵌入式根文件系统。