强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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

特性initramfsinitrd
格式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可选软件
/rootroot 用户目录

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/console5, 1控制台终端
/dev/null1, 3空设备(丢弃输出)
/dev/zero1, 5零设备(返回零)
/dev/tty5, 0当前终端
/dev/tty04, 0当前虚拟控制台
/dev/random1, 8随机数(阻塞)
/dev/urandom1, 9随机数(非阻塞)
/dev/ptmx5, 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 本章小结

概念说明
rootfsLinux 启动时挂载的第一个文件系统
FHS文件系统层次标准,定义目录结构
devtmpfs内核自动管理设备节点的虚拟文件系统
initramfs内存中的初始 rootfs,用于启动
squashfs压缩只读文件系统,适合嵌入式

扩展阅读


上一章: 第 3 章 — 基本用法
下一章: 第 5 章 — Init 系统详解