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 简单 一般 apt Debian/Ubuntu 衍生系统

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

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

2.1 准备环境与源码

  1. 安装基础依赖
bash 复制代码
sudo apt install build-essential gcc-arm-linux-gnueabihf bzip2 xz-utils squashfs-tools
  1. 下载并解压 BusyBox 源码
bash 复制代码
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1

2.2 编译 BusyBox

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

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

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

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

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

2.3 搭建根目录结构

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

bash 复制代码
mkdir -p ~/minirootfs
cp -r _install/* ~/minirootfs/
cd ~/minirootfs

补充 FHS 标准必要目录:

bash 复制代码
mkdir -p etc/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

  2. /etc/init.d/rcS(系统初始化脚本)

bash 复制代码
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
hostname -F /etc/hostname
mount -a

添加执行权限:

bash 复制代码
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

  2. /etc/hostname

    minirootfs

  3. /etc/passwd(用户配置)

    root:x:0:0:root:/root:/bin/sh

  4. /etc/group(用户组配置)

    root:x:0:

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

静态编译可跳过此步。

  1. 查看 BusyBox 的库依赖
bash 复制代码
arm-linux-gnueabihf-ldd bin/busybox
  1. 将依赖的库文件复制到 rootfs 的 lib 目录,例如:
bash 复制代码
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 镜像(内核启动用)
bash 复制代码
find . | cpio -o -H newc | gzip -9 > ../rootfs.cpio.gz
场景2:制作 squashfs 只读压缩镜像(量产推荐)
bash 复制代码
mksquashfs ~/minirootfs rootfs.squashfs -comp xz -b 128K
场景3:制作 ext4 读写镜像
bash 复制代码
dd if=/dev/zero of=rootfs.ext4 bs=1M count=128
mkfs.ext4 rootfs.ext4
mkdir -p /mnt/rootfs && mount rootfs.ext4 /mnt/rootfs
cp -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 工具剥离:
bash 复制代码
# 剥离二进制所有符号(量产必做)
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:压缩解压速度快,平衡性能与体积
    极致压缩命令示例:
bash 复制代码
mksquashfs rootfs rootfs.squashfs -comp xz -Xdict-size 128K -b 128K -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 体积分析方法

bash 复制代码
# 查看总大小
du -sh rootfs/
# 逐层定位大目录
du -h --max-depth=1 rootfs/
# 找出最大的10个文件
find rootfs/ -type f -exec du -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 检查依赖;或交叉编译架构与目标不一致
  • 裁剪后功能异常:遵循少量多次原则,逐步回退裁剪项,定位误删的命令或库
相关推荐
余额瞒着我当琳2 小时前
C++STL容器string--迭代器,string的接口,string的遍历,访问方式
c++
小小龙学IT3 小时前
C++ std::vector 底层实现深度解析:内存布局、扩容策略、移动语义与迭代器失效
开发语言·c++·算法
二宝哥3 小时前
CentOS 7.9 离线安装 Nginx 并配置openssl1.1.1
linux·nginx·centos
程序员老陆3 小时前
说一说FFmpeg6在Linux平台的编译
linux·运维·服务器·ffmpeg
码匠许师傅3 小时前
【C++ 面试真题】聊聊 volatile 和 mutable
开发语言·c++·面试
(Charon)3 小时前
【C++】线程安全队列(二):生产者消费者模型与 condition_variable 阻塞等待
c语言·开发语言·c++
IT摆渡者4 小时前
VM EXSI7.0存储硬盘故障处理
大数据·linux·github
赵民勇4 小时前
systemd-cgtop命令详解
linux·运维
10岁的博客4 小时前
【C++题解】最多共线点问题:小飞拿蛋糕游戏的最优策略
c++