BusyBox 搭建 mini rootfs 完全指南 / 第 4 章:rootfs 概念与构建
第 4 章:rootfs 概念与构建
4.1 什么是 rootfs
4.1.1 根文件系统定义
rootfs(Root File System,根文件系统)是 Linux 启动时挂载的第一个文件系统,是整个系统目录树的起点。
Linux 目录树(rootfs 是根节点)
/
├── bin/ → 基本用户命令
├── sbin/ → 系统管理命令
├── etc/ → 系统配置
├── lib/ → 共享库
├── dev/ → 设备文件
├── proc/ → 进程信息(虚拟文件系统)
├── sys/ → 内核对象(虚拟文件系统)
├── tmp/ → 临时文件
├── var/ → 可变数据
├── usr/ → 用户程序
└── home/ → 用户目录
4.1.2 为什么需要 rootfs
| 原因 | 说明 |
|---|
| 内核启动依赖 | 内核启动后需要找到 init 程序 |
| 设备访问 | 需要 /dev 目录访问设备 |
| 配置加载 | 需要 /etc 读取系统配置 |
| 程序运行 | 需要 /bin 和 /lib 运行用户程序 |
| 临时存储 | 需要 /tmp 提供临时空间 |
4.1.3 rootfs 的类型
rootfs 存储形式
├── 磁盘文件系统
│ ├── ext4 → 常用于 SD 卡、硬盘
│ ├── ext3/ext2 → 旧式存储
│ ├── squashfs → 只读压缩文件系统(OpenWrt 常用)
│ └── jffs2 → 闪存文件系统(NAND Flash)
├── 内存文件系统
│ ├── tmpfs → 临时文件系统
│ ├── ramfs → 内存文件系统
│ └── initramfs → 初始内存文件系统
└── 网络文件系统
├── NFS → 网络文件系统(开发调试常用)
└── CIFS → Windows 共享
4.2 Linux 启动与 rootfs
4.2.1 启动流程概览
电源开启
│
▼
┌────────────────┐
│ Bootloader │ (U-Boot / GRUB)
│ 加载内核 │
└───────┬────────┘
│
▼
┌────────────────┐
│ Linux 内核 │
│ 初始化硬件 │
│ 挂载 rootfs │
└───────┬────────┘
│
▼
┌────────────────┐
│ init 进程 │ (PID=1)
│ BusyBox init │
└───────┬────────┘
│
▼
┌────────────────┐
│ 用户空间 │
│ 服务/Shell │
└────────────────┘
4.2.2 内核如何找到 rootfs
# 内核启动参数(bootargs)
# 指定 rootfs 位置
root=/dev/mmcblk0p2 # SD 卡第二个分区
root=/dev/sda1 # SATA 硬盘
root=/dev/nfs # NFS(网络文件系统)
root=LABEL=rootfs # 卷标
# 指定 rootfs 类型
rootfstype=ext4
rootfstype=squashfs
rootfstype=initramfs
# 完整示例
console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw
4.2.3 initramfs vs initrd
| 特性 | initramfs | initrd |
|---|
| 格式 | cpio 归档 | 块设备镜像 |
| 加载 | 解压到内存 | 挂载为块设备 |
| 内容 | 替代 rootfs | 临时 rootfs |
| 用途 | 嵌入式、恢复盘 | 传统桌面/服务器 |
# 创建 initramfs
$ cd _install
$ find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
# 内核启动参数
initrd=initramfs.cpio.gz
4.3 标准 rootfs 目录结构
4.3.1 FHS 标准
Linux 遵循 FHS(Filesystem Hierarchy Standard)规范:
| 目录 | 用途 | 必需 |
|---|
/bin | 基本用户命令 | ✓ |
/sbin | 系统管理命令 | ✓ |
/etc | 系统配置 | ✓ |
/lib | 共享库 | ✓ |
/dev | 设备文件 | ✓ |
/proc | 进程信息 | ✓ |
/sys | 内核对象 | ✓ |
/tmp | 临时文件 | ✓ |
/var | 可变数据 | ✓ |
/usr | 用户程序 | ✓ |
/home | 用户目录 | ✗ |
/mnt | 挂载点 | ✗ |
/opt | 可选软件 | ✗ |
/root | root 用户目录 | ✗ |
4.3.2 嵌入式系统简化目录
# 嵌入式系统最小目录结构
/
├── bin/ # BusyBox + 符号链接
│ ├── busybox
│ ├── sh -> busybox
│ └── ...
├── sbin/ # 系统管理符号链接
│ ├── init -> ../bin/busybox
│ ├── mount -> ../bin/busybox
│ └── ...
├── etc/ # 配置文件
│ ├── inittab # BusyBox init 配置
│ ├── init.d/
│ │ └── rcS # 启动脚本
│ ├── passwd # 用户信息
│ ├── group # 组信息
│ ├── hostname # 主机名
│ └── resolv.conf # DNS 配置
├── lib/ # 共享库(静态编译时可为空)
├── dev/ # 设备文件
│ ├── console
│ ├── null
│ └── tty
├── proc/ # 挂载点
├── sys/ # 挂载点
├── tmp/ # 临时文件
└── var/ # 日志等
├── log/
└── run/
4.4 构建 mini rootfs
4.4.1 完整构建脚本
#!/bin/bash
# create_rootfs.sh - 使用 BusyBox 构建 mini rootfs
set -e
ROOTFS_DIR="${1:-./rootfs}"
BUSYBOX="./busybox"
echo "Creating mini rootfs in $ROOTFS_DIR..."
# 1. 创建目录结构
mkdir -p "$ROOTFS_DIR"/{bin,dev,etc/init.d,lib,mnt,proc,root,sbin,sys,tmp,var/{log,run,lock}}
# 2. 安装 BusyBox
echo "Installing BusyBox..."
install -m 755 "$BUSYBOX" "$ROOTFS_DIR/bin/busybox"
# 3. 创建符号链接
echo "Creating symlinks..."
# bin 目录
APPLETS_BIN="sh ash bash cat chmod chown cp date dd df echo expr
false find grep head hostname id kill ln login ls
mkdir mknod more mount mv nice ping ps pwd rm rmdir
sed sh sleep sort stty su sync tail tee test touch
true umount uname vi wc wget which yes"
for cmd in $APPLETS_BIN; do
ln -sf busybox "$ROOTFS_DIR/bin/$cmd"
done
# sbin 目录
APPLETS_SBIN="adjtimex arp brctl depmod devmem fbsplash freeramdisk
fsck getty halt hwclock ifconfig ifdown ifup init
insmod ip klogd loadfont loadkmap logread lsmod mdev
modinfo modprobe poweroff raidautorun reboot rmmod
route sulogin swapoff swapon sysctl syslogd
telnetd udhcpc udhcpd vconfig watchdog"
for cmd in $APPLETS_SBIN; do
ln -sf ../bin/busybox "$ROOTFS_DIR/sbin/$cmd"
done
# linuxrc(initrd 场景)
ln -sf bin/busybox "$ROOTFS_DIR/linuxrc"
# 4. 创建基本配置文件
echo "Creating configuration files..."
# /etc/inittab
cat > "$ROOTFS_DIR/etc/inittab" << 'EOF'
# /etc/inittab - BusyBox init 配置
# 系统初始化
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -t sysfs sysfs /sys
::sysinit:/bin/mount -t tmpfs tmpfs /tmp
::sysinit:/bin/mount -t devtmpfs devtmpfs /dev
# 运行启动脚本
::sysinit:/etc/init.d/rcS
# 控制台(串口)
console::respawn:-/bin/sh
# 关机/重启
::shutdown:/bin/umount -a -r
::shutdown:/bin/sync
::restart:/sbin/init
EOF
# /etc/init.d/rcS
cat > "$ROOTFS_DIR/etc/init.d/rcS" << 'EOF'
#!/bin/sh
# /etc/init.d/rcS - 系统启动脚本
echo "Starting system..."
# 挂载虚拟文件系统
/bin/mount -t proc proc /proc 2>/dev/null || true
/bin/mount -t sysfs sysfs /sys 2>/dev/null || true
/bin/mount -t tmpfs tmpfs /tmp 2>/dev/null || true
/bin/mount -t devtmpfs devtmpfs /dev 2>/dev/null || true
# 设置主机名
/bin/hostname -F /etc/hostname 2>/dev/null || /bin/hostname "miniroot"
# 启动 syslog
[ -x /sbin/syslogd ] && /sbin/syslogd
[ -x /sbin/klogd ] && /sbin/klogd
# 配置网络(DHCP)
[ -x /sbin/udhcpc ] && /sbin/udhcpc -i eth0 -b
# 设置时区
export TZ="CST-8"
echo "System startup complete."
EOF
chmod 755 "$ROOTFS_DIR/etc/init.d/rcS"
# /etc/passwd
cat > "$ROOTFS_DIR/etc/passwd" << 'EOF'
root::0:0:root:/root:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/false
EOF
# /etc/group
cat > "$ROOTFS_DIR/etc/group" << 'EOF'
root:x:0:
nobody:x:65534:
EOF
# /etc/hostname
echo "miniroot" > "$ROOTFS_DIR/etc/hostname"
# /etc/resolv.conf
cat > "$ROOTFS_DIR/etc/resolv.conf" << 'EOF'
nameserver 8.8.8.8
nameserver 8.8.4.4
EOF
# /etc/fstab
cat > "$ROOTFS_DIR/etc/fstab" << 'EOF'
# <device> <mount> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
devtmpfs /dev devtmpfs defaults 0 0
EOF
# /etc/profile
cat > "$ROOTFS_DIR/etc/profile" << 'EOF'
export PATH="/bin:/sbin:/usr/bin:/usr/sbin"
export PS1='[\u@\h \W]\$ '
export TZ="CST-8"
alias ll='ls -la'
EOF
# 5. 创建设备文件
echo "Creating device nodes..."
sudo mknod -m 622 "$ROOTFS_DIR/dev/console" c 5 1 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/null" c 1 3 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/zero" c 1 5 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/tty" c 5 0 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/tty0" c 4 0 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/tty1" c 4 1 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/random" c 1 8 2>/dev/null || true
sudo mknod -m 666 "$ROOTFS_DIR/dev/urandom" c 1 9 2>/dev/null || true
# 6. 复制共享库(仅动态链接时需要)
echo "Checking dynamic libraries..."
if file "$BUSYBOX" | grep -q "dynamically linked"; then
echo "Copying shared libraries..."
LIBS=$(ldd "$BUSYBOX" | grep -o '/lib[^ ]*' | sort -u)
for lib in $LIBS; do
lib_dir=$(dirname "$lib")
mkdir -p "$ROOTFS_DIR$lib_dir"
cp -L "$lib" "$ROOTFS_DIR$lib"
done
else
echo "BusyBox is statically linked - no libraries needed."
fi
# 7. 设置权限
chmod 1777 "$ROOTFS_DIR/tmp"
chmod 700 "$ROOTFS_DIR/root"
# 8. 统计
echo ""
echo "=== Rootfs Summary ==="
echo "Total size: $(du -sh $ROOTFS_DIR | cut -f1)"
echo "Files: $(find $ROOTFS_DIR -type f | wc -l)"
echo "Symlinks: $(find $ROOTFS_DIR -type l | wc -l)"
echo "Directories: $(find $ROOTFS_DIR -type d | wc -l)"
echo ""
echo "To test with QEMU:"
echo " sudo chroot $ROOTFS_DIR /bin/sh"
4.4.2 运行构建脚本
# 赋予执行权限
$ chmod +x create_rootfs.sh
# 创建 rootfs
$ sudo ./create_rootfs.sh ./myrootfs
# 查看结果
$ ls -la myrootfs/
total 28
drwxr-xr-x 9 root root 4096 Jan 1 10:00 .
drwxr-xr-x 3 user user 4096 Jan 1 10:00 ..
drwxr-xr-x 2 root root 4096 Jan 1 10:00 bin
drwxr-xr-x 2 root root 100 Jan 1 10:00 dev
drwxr-xr-x 3 root root 100 Jan 1 10:00 etc
drwxr-xr-x 2 root root 40 Jan 1 10:00 lib
drwxr-xr-x 2 root root 40 Jan 1 10:00 mnt
drwxr-xr-x 2 root root 40 Jan 1 10:00 proc
drwx------ 2 root root 40 Jan 1 10:00 root
drwxr-xr-x 2 root root 4096 Jan 1 10:00 sbin
drwxr-xr-x 2 root root 40 Jan 1 10:00 sys
drwxrwxrwt 2 root root 40 Jan 1 10:00 tmp
drwxr-xr-x 4 root root 80 Jan 1 10:00 var
4.5 测试 rootfs
4.5.1 使用 chroot 测试
# chroot 进入 rootfs
$ sudo chroot ./myrootfs /bin/sh
# 测试基本功能
/ # ls -la
total 4
drwxr-xr-x 9 root root 4096 Jan 1 02:00 .
drwxr-xr-x 9 root root 4096 Jan 1 02:00 ..
drwxr-xr-x 2 root root 4096 Jan 1 02:00 bin
...
/ # echo $SHELL
/bin/sh
/ # busybox --list | wc -l
342
/ # cat /etc/hostname
miniroot
/ # exit
4.5.2 使用 QEMU 测试
# x86_64 系统测试 x86_64 rootfs
$ sudo chroot ./myrootfs /bin/sh
# ARM 系统测试(需要 qemu-user-static)
$ sudo cp /usr/bin/qemu-arm-static ./myrootfs/usr/bin/
$ sudo chroot ./myrootfs /bin/sh
# 使用 QEMU 系统模式完整启动
$ qemu-system-x86_64 \
-kernel /path/to/bzImage \
-initrd /path/to/initramfs.cpio.gz \
-append "console=ttyS0 root=/dev/ram0" \
-nographic \
-m 256M
4.5.3 打包 rootfs 为 initramfs
# 打包为 cpio 归档
$ cd myrootfs
$ find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz
# 打包大小
$ ls -lh ../initramfs.cpio.gz
-rw-r--r-- 1 user user 1.2M Jan 1 00:00 initramfs.cpio.gz
4.5.4 打包 rootfs 为 ext4 镜像
# 创建 64MB ext4 镜像
$ dd if=/dev/zero of=rootfs.ext4 bs=1M count=64
$ mkfs.ext4 rootfs.ext4
# 挂载并复制
$ sudo mkdir -p /tmp/mnt
$ sudo mount rootfs.ext4 /tmp/mnt
$ sudo cp -a myrootfs/* /tmp/mnt/
$ sudo umount /tmp/mnt
# 使用 QEMU 测试
$ qemu-system-x86_64 \
-kernel /path/to/bzImage \
-drive file=rootfs.ext4,format=raw \
-append "console=ttyS0 root=/dev/sda rw" \
-nographic \
-m 256M
4.6 设备文件详解
4.6.1 必要的设备文件
| 设备 | 主/次设备号 | 用途 |
|---|
/dev/console | 5, 1 | 控制台终端 |
/dev/null | 1, 3 | 空设备(丢弃输出) |
/dev/zero | 1, 5 | 零设备(返回零) |
/dev/tty | 5, 0 | 当前终端 |
/dev/tty0 | 4, 0 | 当前虚拟控制台 |
/dev/random | 1, 8 | 随机数(阻塞) |
/dev/urandom | 1, 9 | 随机数(非阻塞) |
/dev/ptmx | 5, 2 | 伪终端主设备 |
4.6.2 mdev 动态设备管理
# /etc/mdev.conf - mdev 配置
# 格式:<设备名> <uid>:<gid> <权限> [=<替换路径>] [>|<路径>] [@|$|*<命令>]
# 示例
null root:root 666
zero root:root 666
console root:root 600
random root:root 666
urandom root:root 666
# 网络设备
eth[0-9]* root:root 600
# 块设备
sd[a-z][0-9]* root:disk 660
# 自动运行脚本
# 当插入 USB 设备时自动挂载
sd[a-z][0-9]* root:disk 660 */bin/mdev-mount.sh
4.6.3 使用 devtmpfs
# /etc/init.d/rcS 中启用 devtmpfs
#!/bin/sh
# 挂载 devtmpfs(内核自动创建设备节点)
mount -t devtmpfs devtmpfs /dev
# 或者使用 mdev
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
4.7 rootfs 优化技巧
4.7.1 减小 rootfs 体积
# 使用 squashfs 压缩
$ mksquashfs myrootfs rootfs.sqsh -comp xz -b 256K
# 查看压缩比
$ du -sh myrootfs/
15M myrootfs/
$ ls -lh rootfs.sqsh
-rw-r--r-- 1 user user 3.2M Jan 1 00:00 rootfs.sqsh
4.7.2 分离可写区域
# 只读 rootfs + 可写 overlay
mount -t squashfs rootfs.sqsh /mnt/rootfs
mount -t tmpfs tmpfs /mnt/rootfs/tmp
mount -t overlay overlay \
-o lowerdir=/mnt/rootfs,upperdir=/tmp/upper,workdir=/tmp/work \
/mnt/merged
4.7.3 只读 rootfs 配置
# /etc/fstab 配置只读
/dev/mmcblk0p2 / ext4 ro,noatime 0 1
tmpfs /tmp tmpfs defaults 0 0
tmpfs /var tmpfs defaults 0 0
4.8 常见问题排查
4.8.1 Kernel panic - not syncing: No working init found
# 原因:内核找不到 init 程序
# 检查项:
# 1. BusyBox 是否正确安装
ls -la rootfs/bin/busybox
# 2. init 符号链接是否存在
ls -la rootfs/sbin/init
# 应该指向 ../bin/busybox
# 3. 内核启动参数是否正确
# root=/dev/xxx 应指向正确的 rootfs
4.8.2 Kernel panic - not syncing: VFS: Unable to mount root fs
# 原因:内核无法挂载 rootfs
# 检查项:
# 1. 文件系统驱动是否编译进内核
# 2. root= 参数是否正确
# 3. rootfstype= 参数是否正确
4.8.3 /bin/sh: not found
# 原因:动态链接的 BusyBox 找不到共享库
# 解决方案:
# 1. 使用静态编译
# 2. 确保共享库在 rootfs 的 /lib 中
# 检查依赖
$ ldd rootfs/bin/busybox
linux-vdso.so.1 (0x00007ffd...)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f...)
/lib64/ld-linux-x86-64.so.2 (0x00007f...)
# 复制依赖库
$ for lib in $(ldd rootfs/bin/busybox | grep -o '/lib[^ ]*'); do
cp -L "$lib" "rootfs$lib"
done
4.9 本章小结
| 概念 | 说明 |
|---|
| rootfs | Linux 启动时挂载的第一个文件系统 |
| FHS | 文件系统层次标准,定义目录结构 |
| devtmpfs | 内核自动管理设备节点的虚拟文件系统 |
| initramfs | 内存中的初始 rootfs,用于启动 |
| squashfs | 压缩只读文件系统,适合嵌入式 |
扩展阅读
上一章: 第 3 章 — 基本用法
下一章: 第 5 章 — Init 系统详解