title: 根文件系统构建 tags: [根文件系统, BusyBox, 嵌入式Linux, I.MX6ULL, 系统移植] created: 2026-09-16 updated: 2026-09-17
💡 关联知识: [[06-系统移植与启动/03-Linux内核裁剪与配置]] | [[02-嵌入式Linux内核基础/01-内核编译与启动流程]] | [[06-系统移植与启动/01-Uboot移植与配置]]
根文件系统(rootfs)是 Linux 内核启动后挂载的第一个文件系统。它包含了系统启动所需的目录结构、基本命令、配置文件和库文件,为用户空间程序提供运行环境。
关键特性:
/sbin/init 或指定的 init 程序与普通文件系统的区别:
Linux 根文件系统采用标准的目录结构,每个目录有特定用途:
| 目录 | 用途 | 说明 |
|---|---|---|
/bin |
基本命令 | 系统启动和运行必需的可执行文件,如 ls、mv、cp |
/sbin |
系统命令 | 系统管理命令,如 ifconfig、reboot,通常只有管理员使用 |
/lib |
共享库 | 系统库文件,供应用程序动态链接使用 |
/etc |
配置文件 | 系统配置文件,如 fstab、inittab、resolv.conf |
/dev |
设备文件 | 设备节点文件,如 /dev/ttyS0、/dev/null |
/proc |
进程信息 | proc 文件系统挂载点,存储系统运行时信息 |
/sys |
内核对象 | sysfs 文件系统挂载点,提供内核数据结构信息 |
/usr |
用户程序 | Unix Software Resource,用户空间程序和库 |
/var |
可变数据 | 日志、缓存等可改变的数据 |
/mnt |
挂载点 | 临时挂载目录,如 /mnt/sdcard、/mnt/usb |
/tmp |
临时文件 | 系统重启后清空的临时文件 |
/root |
root用户目录 | root 用户的家目录 |
嵌入式 Linux 常用的文件系统类型:
磁盘文件系统:
闪存文件系统:
内存文件系统:
网络文件系统:
1. 下载并编译 BusyBox(提供基本命令)
2. 创建根文件系统目录结构
3. 拷贝 C 库文件(libc、libm 等)
4. 创建必要的设备节点
5. 编写启动脚本(rcS、inittab 等)
6. 测试与调试
BusyBox 是一个集成了大量 Linux 命令和工具的单个可执行文件。它将 ls、mv、cp、ifconfig 等常用命令集成到一个程序中,大大减小了根文件系统的体积。
名称由来:"Busy"(繁忙)+ "Box"(盒子),意为"繁忙的盒子",因为它提供了文件系统所需的大量工具。
优势:
# 下载 BusyBox 源码(推荐使用 1.29.0 版本,稳定无 bug)
wget https://busybox.net/downloads/busybox-1.29.0.tar.bz2
# 解压源码
tar -vxjf busybox-1.29.0.tar.bz2
# 进入源码目录
cd busybox-1.29.0
⚠️ 注意:建议使用 1.29.0 版本,该版本经过充分测试,稳定性好。
与 Uboot 和 Linux 内核类似,BusyBox 的编译需要指定交叉编译器。修改 Makefile:
# 第 164 行左右
CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
# 第 190 行左右
ARCH ?= arm
说明:
CROSS_COMPILE:交叉编译器前缀路径(使用绝对路径防止出错)ARCH:目标架构,ARM 芯片设置为 arm默认编译的 BusyBox 在终端中无法正确显示中文字符(显示为 ?)。需要修改源码:
修改文件 1:busybox-1.29.0/libbb/printable_string.c
找到 printable_string 函数,注释掉第 31-32 行和第 45-46 行:
// 修改前(第 31-32 行)
if (c >= 0x7f)
break;
// 修改后
/* if (c >= 0x7f)
break; */
// 修改前(第 45-46 行)
if (c < ' ' || c >= 0x7f)
*d = '?';
// 修改后
if( c < ' ')
*d = '?';
修改文件 2:busybox-1.29.0/libbb/unicode.c
找到 unicode_conv_to_printable2 函数,修改第 1022 行和第 1030-1031 行:
// 修改前(第 1022 行)
*d++ = (c >= ' ' && c < 0x7f) ? c : '?';
// 修改后
*d++ = (c >= ' ') ? c : '?';
// 修改前(第 1030-1031 行)
if (c < ' ' || c >= 0x7f)
*d = '?';
// 修改后
if(c < ' ')
*d = '?';
修改原理:BusyBox 默认会过滤掉 ASCII 码大于 0x7f 的字符(即中文字符),通过注释掉这些判断,可以使 BusyBox 正确显示中文。
BusyBox 支持三种配置方式:
# 1. 默认配置(推荐初学者)
make defconfig
# 2. 全选配置(包含所有功能)
make allyesconfig
# 3. 最小配置(只包含最基本功能)
make allnoconfig
图形化配置(推荐):
make menuconfig
关键配置选项:
1. 静态编译 vs 动态编译:
Location:
-> Settings
-> Build static binary (no shared libs)
⚠️ 重要:不要选择静态编译!静态编译会导致 DNS 解析失败,无法正常联网。必须使用动态编译。
2. vi 风格行编辑:
Location:
-> Settings
-> vi-style line editing commands
3. 简化 modutils(取消选择):
Location:
-> Linux Module Utilities
-> Simplified modutils
4. 启用 mdev(确保选中):
Location:
-> Linux System Utilities
-> mdev (16 kb)
5. 启用 Unicode 支持(可选):
Location:
-> Settings
-> Support Unicode [Y]
-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables [Y]
# 编译 BusyBox
make
# 安装到指定目录
make install CONFIG_PREFIX=/home/zuozhongkai/linux/nfs/rootfs
CONFIG_PREFIX 指定安装目录,即根文件系统所在路径。
安装完成后,rootfs 目录结构如下:
rootfs/
├── bin/ # BusyBox 命令链接
├── sbin/ # 系统命令链接
├── usr/ # 用户程序
│ ├── bin/
│ └── sbin/
└── linuxrc # init 程序(由 BusyBox 生成)
BusyBox 支持的命令可以分为几类:
| 类别 | 命令示例 | 说明 |
|---|---|---|
| 文件操作 | ls, cp, mv, rm, mkdir | 基本文件管理 |
| 文本处理 | cat, grep, sed, awk | 文本查看和处理 |
| 网络工具 | ifconfig, ping, wget | 网络配置和测试 |
| 系统管理 | ps, kill, top, free | 进程和系统管理 |
| 存储工具 | mount, fdisk, df | 存储设备管理 |
在 rootfs 目录下创建必要的目录:
cd rootfs
# 创建基本目录
mkdir -p bin sbin lib etc usr dev proc sys tmp mnt root
# 创建 usr 子目录
mkdir -p usr/bin usr/sbin usr/lib
# 创建 dev 子目录(用于 mdev)
mkdir -p dev/pts
Linux 系统需要基本的设备节点才能正常运行:
cd dev
# 控制台设备
sudo mknod console c 5 1
# 空设备
sudo mknod null c 1 3
# 终端设备
sudo mknod ttyS0 c 4 64
# 随机数设备
sudo mknod random c 1 8
# _urandom 设备
sudo mknod urandom c 1 9
cd ..
设备号说明:
/etc 目录包含系统配置文件,是根文件系统中最关键的目录之一。
创建 /etc/passwd 文件:
# 用户信息文件
cat > etc/passwd << 'EOF'
root::0:0:root:/root:/bin/sh
EOF
创建 /etc/group 文件:
# 用户组信息文件
cat > etc/group << 'EOF'
root:x:0:
EOF
创建 /etc/profile 文件:
cat > etc/profile << 'EOF'
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export runlevel=S
export umask=022
EOF
rcS 是系统启动后执行的第一个脚本,用于初始化系统:
cat > etc/init.d/rcS << 'EOF'
#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export PATH LD_LIBRARY_PATH
mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
EOF
chmod 777 etc/init.d/rcS
脚本说明:
fstab 文件定义了需要自动挂载的文件系统:
cat > etc/fstab << 'EOF'
#<file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
EOF
字段说明:
<file system>:要挂载的设备或文件系统<mount point>:挂载点<type>:文件系统类型<options>:挂载选项(defaults 表示使用默认选项)<dump>:是否备份(0=不备份)<pass>:开机时是否检查(0=不检查)inittab 文件定义了 init 进程的行为:
cat > etc/inittab << 'EOF'
#etc/inittab
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
EOF
字段格式:<id>:<runlevels>:<action>:<process>
action 类型说明:
| action | 说明 |
|---|---|
| sysinit | 系统初始化,执行一次 |
| respawn | 进程终止后重新启动 |
| askfirst | 显示 "Please press Enter to activate this console",等待用户按回车 |
| wait | 等待进程执行完毕 |
| once | 执行一次,不等待 |
| restart | init 重启时执行 |
| ctrlaltdel | 按 Ctrl+Alt+Del 时执行 |
| shutdown | 关机时执行 |
嵌入式 Linux 系统需要 C 库来支持动态链接的程序。常用的 C 库有:
I.MX6ULL 开发板使用 Linaro 工具链,对应的 C 库位于:
/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
# 进入库文件目录
cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
# 拷贝所有动态库和静态库到 rootfs/lib
cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/lib/ -d
重要:修复 ld-linux-armhf.so.3 链接
ld-linux-armhf.so.3 是动态链接器,必须是实际文件而非符号链接:
# 删除 rootfs/lib 中的符号链接
rm ld-linux-armhf.so.3
# 从工具链目录拷贝实际文件
cp /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib/ld-linux-armhf.so.3 /home/zuozhongkai/linux/nfs/rootfs/lib/
# 进入 usr/lib 库文件目录
cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
# 拷贝所有库文件到 rootfs/usr/lib
cp *so* *.a /home/zuozhongkai/linux/nfs/rootfs/usr/lib/ -d
# 查看库文件大小
cd rootfs
du ./lib ./usr/lib/ -sh
# 示例输出:
# 57M ./lib
# 67M ./usr/lib
💡 提示:库文件总大小约 124MB,对于 256MB/512MB NAND Flash 的开发板可能空间不足,建议使用 EMMC 版本。
#!/bin/bash
# rootfs_build.sh - 一键构建根文件系统
# 配置参数
TOOLCHAIN_DIR=/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf
ROOTFS_DIR=/home/zuozhongkai/linux/nfs/rootfs
BUSYBOX_VERSION=1.29.0
# 1. 创建 rootfs 目录
echo "1. 创建 rootfs 目录..."
mkdir -p $ROOTFS_DIR
# 2. 解压并编译 BusyBox
echo "2. 编译 BusyBox..."
tar -vxjf busybox-${BUSYBOX_VERSION}.tar.bz2
cd busybox-${BUSYBOX_VERSION}
# 修改 Makefile
sed -i "s|^CROSS_COMPILE ?=.*|CROSS_COMPILE ?= ${TOOLCHAIN_DIR}/bin/arm-linux-gnueabihf-|" Makefile
sed -i "s|^ARCH ?=.*|ARCH ?= arm|" Makefile
# 配置并编译
make defconfig
make -j$(nproc)
make install CONFIG_PREFIX=$ROOTFS_DIR
cd ..
# 3. 创建目录结构
echo "3. 创建目录结构..."
cd $ROOTFS_DIR
mkdir -p bin sbin lib etc usr dev proc sys tmp mnt root
mkdir -p usr/bin usr/sbin usr/lib dev/pts
# 4. 创建设备节点
echo "4. 创建设备节点..."
cd dev
sudo mknod console c 5 1
sudo mknod null c 1 3
sudo mknod ttyS0 c 4 64
sudo mknod random c 1 8
sudo mknod urandom c 1 9
cd ..
# 5. 拷贝库文件
echo "5. 拷贝库文件..."
cp ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/libc/lib/*so* ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/libc/lib/*.a $ROOTFS_DIR/lib/ -d
cp ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/libc/usr/lib/*so* ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/libc/usr/lib/*.a $ROOTFS_DIR/usr/lib/ -d
# 修复 ld-linux-armhf.so.3
rm -f $ROOTFS_DIR/lib/ld-linux-armhf.so.3
cp ${TOOLCHAIN_DIR}/arm-linux-gnueabihf/libc/lib/ld-linux-armhf.so.3 $ROOTFS_DIR/lib/
# 6. 创建配置文件
echo "6. 创建配置文件..."
# /etc/passwd
cat > etc/passwd << 'EOF'
root::0:0:root:/root:/bin/sh
EOF
# /etc/group
cat > etc/group << 'EOF'
root:x:0:
EOF
# /etc/profile
cat > etc/profile << 'EOF'
export PATH=/sbin:/bin:/usr/sbin:/usr/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export runlevel=S
export umask=022
EOF
# /etc/init.d/rcS
mkdir -p etc/init.d
cat > etc/init.d/rcS << 'EOF'
#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
export PATH LD_LIBRARY_PATH
mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
EOF
chmod 777 etc/init.d/rcS
# /etc/fstab
cat > etc/fstab << 'EOF'
#<file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
EOF
# /etc/inittab
cat > etc/inittab << 'EOF'
#etc/inittab
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
::shutdown:/sbin/swapoff -a
EOF
# /etc/resolv.conf(DNS 配置)
cat > etc/resolv.conf << 'EOF'
nameserver 114.114.114.114
nameserver 192.168.1.1
EOF
echo "7. 根文件系统构建完成!"
echo " 目录:$ROOTFS_DIR"
# 1. 赋予执行权限
chmod +x rootfs_build.sh
# 2. 执行脚本
./rootfs_build.sh
# 3. 验证构建结果
ls -la $ROOTFS_DIR
# 查看 rootfs 总大小
du -sh $ROOTFS_DIR
# 查看各目录大小
du -sh $ROOTFS_DIR/*
NFS(Network File System)是开发阶段最常用的测试方式,无需烧写到 Flash。
配置 Uboot 环境变量:
# 在 Uboot 命令行执行
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.250:/home/zuozhongkai/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.251:192.168.1.250:192.168.1.1:255.255.255.0::eth0:off'
saveenv
boot
NFS 挂载参数说明:
| 参数 | 说明 | 示例值 |
|---|---|---|
root=/dev/nfs |
指定 NFS 根文件系统 | - |
nfsroot=<server-ip>:<path> |
NFS 服务器 IP 和路径 | 192.168.1.250:/home/.../rootfs |
proto=tcp |
使用 TCP 协议 | - |
rw |
读写权限 | - |
ip=<client-ip>:<server-ip>:<gw>:<mask>::<dev>:off |
网络配置 | 192.168.1.251:... |
系统启动后,应该能够进入 shell 环境:
Please press Enter to activate this console.
# ls
bin dev etc lib linuxrc mnt proc root sbin sys tmp usr
#
1. 测试动态链接程序:
# 在 Ubuntu 上编译测试程序
arm-linux-gnueabihf-gcc hello.c -o hello
# 拷贝到 rootfs/drivers 目录
cp hello $ROOTFS_DIR/drivers/
# 在开发板上运行
cd /drivers
./hello
2. 测试中文字符显示:
# 创建中文测试文件
mkdir 测试目录
touch 测试文档.txt
echo "你好世界" > 测试文档.txt
# 验证显示
ls
cat 测试文档.txt
3. 测试网络功能:
# 配置 IP 地址
ifconfig eth0 192.168.1.251
# 测试 ping
ping www.baidu.com
问题 1:can't run '/etc/init.d/rcS': No such file or directory
chmod 777 /etc/init.d/rcS问题 2:中文字符显示为 '?'
printable_string.c 和 unicode.c问题 3:ping 显示 "bad address"
/etc/resolv.conf 文件,配置 DNS 服务器问题 4:动态链接程序无法运行
ld-linux-armhf.so.3| 特性 | I.MX6ULL | STM32MP1 | RK3568 |
|---|---|---|---|
| 架构 | ARM Cortex-A7 | ARM Cortex-A7 | ARM Cortex-A55 |
| 位宽 | 32-bit | 32-bit | 64-bit |
| 交叉编译器 | arm-linux-gnueabihf- | arm-openstlinux-linux-gnueabi- | aarch64-linux-gnu- |
| C 库 | glibc | glibc | glibc |
| 文件系统类型 | ext4/squashfs | ext4 | ext4/squashfs |
| 推荐构建工具 | BusyBox/Buildroot | Yocto/Buildroot | Buildroot/Debian |
| 根 FS 大小 | 50-200MB | 100-500MB | 200MB-2GB |
| 工具 | 特点 | 适用场景 |
|---|---|---|
| BusyBox | 手动构建,完全控制 | 学习、小型系统 |
| Buildroot | 自动化构建,配置简单 | 中小型项目 |
| Yocto | 功能强大,学习曲线陡峭 | 大型商业项目 |
| Debian | 完整 Linux 发行版 | 需要丰富软件包 |
| 优化方法 | 效果 | 说明 |
|---|---|---|
| 使用 BusyBox 替代完整工具 | 减小 50-70% | BusyBox 集成多命令 |
| 使用 squashfs 压缩 | 减小 50-60% | 只读压缩文件系统 |
| 去除未使用的库 | 减小 20-40% | 精简 C 库 |
| 使用 uClibc 替代 glibc | 减小 30-50% | 更小的 C 库 |
考察点:基础概念理解
参考答案: 根文件系统(rootfs)是 Linux 内核启动时挂载的第一个文件系统,包含系统启动必需的目录结构、命令、库文件和配置文件。
与普通文件系统的区别:
考察点:工具理解
参考答案: BusyBox 是一个集成了大量 Linux 命令和工具的单个可执行文件,被称为"嵌入式 Linux 的瑞士军刀"。
常用原因:
考察点:实际问题解决
参考答案:
BusyBox 默认会过滤 ASCII 码大于 0x7f 的字符(即非 ASCII 字符),导致中文字符显示为 ?。
修改原理:
printable_string.c 中的 printable_string 函数会将 >= 0x7f 的字符替换为 ?unicode.c 中的 unicode_conv_to_printable2 函数也有类似逻辑修改后的效果:
考察点:配置选项理解
参考答案: 静态编译会将所有库函数直接链接到可执行文件中,虽然简化了依赖,但会导致严重问题:
DNS 解析失败:
ping www.baidu.com 等网络命令失败体积增大:
内存占用:
最佳实践:使用动态编译,确保 /lib 目录包含必要的动态库。
考察点:系统配置能力
参考答案: 需要配置 mdev 和相应的挂载点:
1. 确保 mdev 配置正确:
# /etc/init.d/rcS
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
2. 创建挂载点:
mkdir -p /mnt/usb /mnt/sdcard
3. 配置 mdev 规则(可选):
# /etc/mdev.conf
sd[a-z][0-9]* 0:0 0660 */bin/mount /dev/$MDEV /mnt/sdcard
sd[a-z] 0:0 0660 */bin/umount /mnt/sdcard
4. 配置 fstab(可选):
# /etc/fstab
/dev/sda1 /mnt/usb vfat defaults 0 0
/dev/mmcblk0p1 /mnt/sdcard vfat defaults 0 0
验证方法:
ls /mnt/usb 或 ls /mnt/sdcard最后更新: 2026-09-17