三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

rootfs 详解与裁剪优化记录

rootfs 详解与裁剪优化记录

rootfs 详解与裁剪优化记录(以busybox为例)

rootfs(Root File System,根文件系统)是 Linux 内核启动后挂载的第一个文件系统(挂载点为/),是整个用户空间的运行根基。内核完成硬件初始化后,会挂载 rootfs 并执行其中的init进程,正式进入用户态。它包含了系统运行必需的初始化程序、命令工具、动态库、配置文件、设备节点等所有核心组件。

一、rootfs 核心基础

1.1 核心组成

  • init 进程:系统第一个用户进程(PID=1),负责系统初始化与服务管理
  • 基础命令集:shell、文件操作、系统管理等工具(如 ls、mount、sh)
  • 动态链接库:libc、libm 等运行时依赖库
  • 系统配置/etc目录下的主机名、挂载表、用户权限、启动脚本等
  • 设备节点/dev下的硬件访问接口
  • 虚拟文件系统挂载点/proc/sys/tmp

1.2 常见类型与适用场景

类型特点适用场景
initramfs/initrd内存文件系统,内核启动时加载到 RAM过渡启动、极小系统、ram 运行场景
ext4通用读写文件系统,稳定性好eMMC、SD 卡等块设备
squashfs只读高压缩文件系统,体积极小嵌入式固件、只读系统分区
ubifs/yaffs2专为 NAND Flash 设计,支持磨损均衡嵌入式 Flash 存储
NFS rootfs网络挂载开发调试阶段

1.3 主流构建方案对比

方案上手难度体积控制包管理适用场景
BusyBox 手动构建中等极致裁剪原理学习、极小系统、深度定制
Buildroot简单优秀有限嵌入式中小项目、快速量产
Yocto/Poky困难优秀完整工业级项目、复杂系统定制
Debootstrap简单一般aptDebian/Ubuntu 衍生系统

二、从零构建最小 rootfs(BusyBox 分步实操)

本步骤以 ARM 架构交叉编译为例,x86_64 平台可直接使用本地 gcc,流程完全一致。

2.1 准备环境与源码

  1. 安装基础依赖
sudoaptinstallbuild-essential gcc-arm-linux-gnueabihfbzip2xz-utils squashfs-tools
  1. 下载并解压 BusyBox 源码
wgethttps://busybox.net/downloads/busybox-1.36.1.tar.bz2tarxf busybox-1.36.1.tar.bz2&&cdbusybox-1.36.1

2.2 编译 BusyBox

推荐优先使用静态编译,减少动态库依赖问题;追求极致体积可选动态编译。

方式一:静态编译(推荐新手)
  1. 进入配置界面
makeARCH=armCROSS_COMPILE=arm-linux-gnueabihf- menuconfig
  1. 核心配置
  • Settings -> Build static binary (no shared libs)勾选
  • Settings -> Cross compiler prefix填写工具链前缀:arm-linux-gnueabihf-
  • (可选裁剪)在Applets分类中关闭不需要的命令
  1. 编译安装
makeARCH=armCROSS_COMPILE=arm-linux-gnueabihf-install

编译完成后,产物会生成在_install目录中。

方式二:动态编译(体积更小)

步骤与静态一致,仅不勾选「Build static binary」,后续需补充依赖库。

2.3 搭建根目录结构

创建 rootfs 工作目录并复制 BusyBox 产物:

mkdir-p~/minirootfscp-r_install/* ~/minirootfs/cd~/minirootfs

补充 FHS 标准必要目录:

mkdir-petc/init.d dev proc sys tmp var/log var/run root home lib usr/lib

2.4 配置核心系统文件

/etc目录的配置是系统正常启动的关键,逐个创建以下文件:

  1. /etc/inittab(init 进程配置)
::sysinit:/etc/init.d/rcS ::askfirst:-/bin/sh ::restart:/sbin/init ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r
  1. /etc/init.d/rcS(系统初始化脚本)
#!/bin/shmount-tproc proc /procmount-tsysfs sysfs /sysmount-tdevtmpfs devtmpfs /devhostname-F/etc/hostnamemount-a

添加执行权限:

chmod+x etc/init.d/rcS
  1. /etc/fstab(文件系统挂载表)
proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 devtmpfs /dev devtmpfs defaults 0 0 tmpfs /tmp tmpfs defaults 0 0
  1. /etc/hostname
minirootfs
  1. /etc/passwd(用户配置)
root:x:0:0:root:/root:/bin/sh
  1. /etc/group(用户组配置)
root:x:0:

2.5 补齐动态库依赖(仅动态编译需要)

静态编译可跳过此步。

  1. 查看 BusyBox 的库依赖
arm-linux-gnueabihf-ldd bin/busybox
  1. 将依赖的库文件复制到 rootfs 的lib目录,例如:
cp/usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3 lib/cp/usr/arm-linux-gnueabihf/lib/libc.so.6 lib/cp/usr/arm-linux-gnueabihf/lib/libm.so.6 lib/

2.6 打包生成镜像

根据使用场景选择对应格式:

场景1:制作 initramfs 镜像(内核启动用)
find.|cpio-o-Hnewc|gzip-9>../rootfs.cpio.gz
场景2:制作 squashfs 只读压缩镜像(量产推荐)
mksquashfs ~/minirootfs rootfs.squashfs-compxz-b128K
场景3:制作 ext4 读写镜像
ddif=/dev/zeroof=rootfs.ext4bs=1Mcount=128mkfs.ext4 rootfs.ext4mkdir-p/mnt/rootfs&&mountrootfs.ext4 /mnt/rootfscp-r~/minirootfs/* /mnt/rootfs/umount/mnt/rootfs

三、rootfs 裁剪核心步骤(逐层瘦身)

裁剪遵循「先功能模块 → 再二进制库 → 再文件系统 → 最后启动流程」的优先级,从大体积项入手收益最高。

3.1 第一层:功能模块裁剪(体积减少 50%+)

核心原则:只保留系统必需功能,移除所有非必要组件。

  1. BusyBox 命令集深度裁剪
    BusyBox 默认包含上百个命令,绝大多数场景无需使用。进入make menuconfig按需关闭:
  • 无网络需求:关闭Networking Utilities全部分类(ping、ifconfig、wget 等)
  • 无文件高级操作:关闭 diff、patch、cpio、tar 等
  • 无进程高级管理:关闭 top、strace、ps 高级选项
  • 无模块加载需求:关闭Linux Module Utilities
  • 无打印、多媒体、蓝牙相关功能:全部关闭
    极致裁剪后,仅保留 init、sh、mount、ls、cp、rm 等核心命令,静态编译体积可压缩到 200KB 以内。
  1. 移除冗余系统服务
  • 日志服务:不需要日志可关闭 syslogd、klogd
  • 定时任务:不需要 cron 则直接移除
  • 网络服务:sshd、ftp、telnetd 等按需保留
  • 登录认证:嵌入式场景可去掉 login、passwd,直接 root 自动登录
  1. C 库选型优化(底层体积关键)
  • glibc:功能最全但体积最大,完整动态库约 8MB
  • musl libc:轻量级 POSIX 兼容库,完整动态库仅几百 KB,静态编译优势极大
  • uClibc-ng:面向嵌入式,兼容性好,体积略大于 musl
    嵌入式裁剪首选 musl libc,同等功能下体积比 glibc 小 70% 以上。

3.2 第二层:二进制与库文件裁剪(体积减少 20%~40%)

  1. strip 剥离符号信息
    编译生成的二进制、动态库包含大量调试符号与符号表,占用显著体积,使用 strip 工具剥离:
# 剥离二进制所有符号(量产必做)arm-linux-gnueabihf-strip --strip-all bin/* sbin/* usr/bin/* usr/sbin/*# 剥离库文件冗余符号(保留动态链接必需信息)arm-linux-gnueabihf-strip --strip-unneeded lib/*.so* usr/lib/*.so*

通常 strip 后体积可减少 30% 左右,缺点是无法进行源码级调试。

  1. 清理冗余库文件
  • 只保留程序实际依赖的.so库,不复制完整 lib 目录
  • 删除所有静态库文件(.a后缀),运行时系统完全不需要
  • 移除库的调试版本、冗余版本号软链接
  1. 编译选项优化
    所有组件编译时添加大小优化参数:
  • CFLAGS 添加-Os:编译器优先优化代码大小
  • 添加-g0:不生成调试符号
  • C++ 程序添加-fno-exceptions -fno-rtti:关闭异常与运行时类型信息

3.3 第三层:文件系统级裁剪(体积减少 50%+)

  1. 选择高压缩只读文件系统
    固件系统分区优先使用squashfs,支持多种压缩算法:
  • xz:压缩率最高,比 ext4 小 60%~80%,适合体积优先场景
  • zstd:压缩解压速度快,平衡性能与体积
    极致压缩命令示例:
mksquashfs rootfs rootfs.squashfs-compxz -Xdict-size 128K-b128K -no-exports -no-xattrs

如需可写配置,推荐「squashfs 只读系统 + overlayfs 可写叠加」方案,兼顾体积与配置持久化。

  1. 清理非运行必需文件
    逐项删除无用内容:
  • 文档手册:/usr/share/man/usr/share/doc、README、LICENSE
  • 多语言文件:/usr/share/locale,仅保留需要的语言
  • 头文件:/usr/include,运行时完全不需要
  • 示例程序、测试脚本、图标主题
  • 缓存与日志:/var/cache/var/log/*

3.4 第四层:启动流程精简

  1. 替换重型 init 系统
    用 BusyBox init 替代 systemd、SysVinit,体积从几 MB 降到几十 KB;极简场景甚至可用自定义脚本直接作为 init 进程。

  2. 精简初始化脚本
    rcS 脚本只保留必需操作:

  • 仅挂载核心虚拟文件系统
  • 移除不必要的硬件检测、模块加载
  • 去掉启动欢迎信息、冗余日志打印
  • 不启动多余后台服务
  1. 简化登录流程
    嵌入式场景无需登录验证时,修改 inittab 将askfirst改为respawn:/bin/sh,启动后直接进入 shell。

四、rootfs 性能优化(启动速度 + 运行效率)

4.1 启动速度优化

  1. 精简 initramfs:无需 initramfs 的场景直接从 Flash 挂载 rootfs,省去内存拷贝与解压开销
  2. 并行启动服务:无依赖的服务后台并行启动,避免串行等待
  3. 驱动内置化:将必需驱动编译进内核(builtin),减少模块加载开销,移除 rootfs 中无用内核模块
  4. 临时目录内存化/tmp/var/run/var/log挂载 tmpfs,大幅提升 IO 速度

4.2 运行时性能优化

  1. 挂载参数优化:添加noatimenodiratime,禁止更新文件访问时间,减少 Flash 写入
  2. 动态库预链接:使用prelink提前完成动态链接重定位,缩短程序启动时间
  3. 日志降频写入:减少日志写入频率,或先写入 tmpfs 定期落盘,降低 Flash 磨损
  4. 内存优化:精简后台进程,关闭无用内核功能,减少内存占用与碎片

五、Buildroot 下的 rootfs 裁剪实战

实际项目中 Buildroot 是最主流的构建工具,以下是核心优化步骤:

5.1 基础配置优化

执行make menuconfig进入配置:

  1. Build options

    • 勾选strip target binaries
    • gcc optimization level选择Optimize for size (-Os)
    • 关闭compile packages with debugging symbols
  2. Toolchain

    • C library 选择musl
    • 关闭Thread library debugging support
    • 关闭Locale supportC++ support(不需要则关闭)
  3. System configuration

    • Init system 选择BusyBox
    • 关闭不必要的登录、getty 功能
    • 自定义精简初始化脚本

5.2 包与 BusyBox 裁剪

  • Target packages中仅勾选必需软件包,默认多余包全部关闭
  • 执行make busybox-menuconfig,按前文方法深度裁剪 BusyBox 命令集

5.3 文件系统镜像优化

进入Filesystem images

  • 仅保留需要的镜像格式(如 squashfs)
  • squashfs 压缩算法选择xz
  • 关闭 ext4、tar 等其他镜像格式

配置完成后执行make,即可生成裁剪优化后的固件镜像。

六、裁剪验证与问题排查

6.1 体积分析方法

# 查看总大小du-shrootfs/# 逐层定位大目录du-h--max-depth=1rootfs/# 找出最大的10个文件findrootfs/-typef-execdu-h{}\;|sort-rh|head-10

6.2 功能验证方式

  1. QEMU 模拟启动:快速验证系统可用性,无需硬件
  2. NFS 网络挂载:开发板通过网络挂载 rootfs,方便调试修改
  3. 逐项功能测试:验证保留的命令、服务是否正常工作

6.3 常见问题定位

  • Kernel panic: No init found:检查/sbin/init是否存在、动态库是否完整、内核是否支持对应文件系统
  • 命令执行报错 not found:动态库缺失,用 ldd 检查依赖;或交叉编译架构与目标不一致
  • 裁剪后功能异常:遵循少量多次原则,逐步回退裁剪项,定位误删的命令或库
← 返回列表