跳转至

文件系统与根文件系统制作:构建嵌入式Linux的基础

学习目标

完成本教程后,你将能够:

  • 理解Linux文件系统的层次结构和标准
  • 掌握常见文件系统类型及其特点
  • 使用BusyBox构建最小根文件系统
  • 配置系统启动脚本和初始化流程
  • 制作initramfs和initrd镜像
  • 优化根文件系统大小和性能
  • 调试根文件系统启动问题
  • 定制适合目标平台的文件系统

前置要求

在开始学习之前,建议你具备:

知识要求: - 熟悉Linux基本命令和操作 - 了解嵌入式Linux系统架构 - 掌握Linux内核编译流程 - 理解设备树和驱动基础

技能要求: - 能够使用交叉编译工具链 - 会使用Makefile构建项目 - 熟悉Shell脚本编程 - 了解文件权限和用户管理

硬件和软件准备: - Linux开发主机(Ubuntu 20.04+推荐) - 交叉编译工具链 - 目标开发板或QEMU模拟器 - 至少2GB可用磁盘空间 - 串口调试工具

Linux文件系统概述

文件系统的作用

文件系统是操作系统用于组织和管理文件的方法,它提供了:

┌─────────────────────────────────────────┐
│         应用程序                         │
│    (读写文件、创建目录等)                 │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│      虚拟文件系统 (VFS)                  │
│    统一的文件操作接口                     │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│      具体文件系统                        │
│  ext4, jffs2, ubifs, tmpfs等            │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│      块设备层/MTD层                      │
│    硬盘、Flash、SD卡等                   │
└─────────────────────────────────────────┘

主要功能: - 文件的存储和组织 - 目录结构管理 - 文件权限和访问控制 - 文件元数据管理 - 空间分配和回收

文件系统层次标准 (FHS)

Linux遵循文件系统层次标准(Filesystem Hierarchy Standard):

/                    # 根目录
├── bin/            # 基本命令二进制文件
├── boot/           # 启动文件(内核、initrd)
├── dev/            # 设备文件
├── etc/            # 系统配置文件
├── home/           # 用户主目录
├── lib/            # 共享库文件
├── media/          # 可移动媒体挂载点
├── mnt/            # 临时挂载点
├── opt/            # 可选应用程序
├── proc/           # 进程信息(虚拟文件系统)
├── root/           # root用户主目录
├── run/            # 运行时数据
├── sbin/           # 系统管理命令
├── srv/            # 服务数据
├── sys/            # 系统信息(虚拟文件系统)
├── tmp/            # 临时文件
├── usr/            # 用户程序和数据
│   ├── bin/        # 用户命令
│   ├── lib/        # 用户库文件
│   ├── local/      # 本地安装的程序
│   └── sbin/       # 用户系统管理命令
└── var/            # 可变数据
    ├── log/        # 日志文件
    ├── tmp/        # 临时文件
    └── run/        # 运行时数据

目录说明

目录 用途 是否必需
/bin 基本命令(ls, cp, cat等) 必需
/sbin 系统管理命令(init, mount等) 必需
/etc 配置文件 必需
/lib 共享库和内核模块 必需
/dev 设备文件 必需
/proc 进程和内核信息 推荐
/sys 设备和驱动信息 推荐
/tmp 临时文件 推荐
/var 可变数据 可选
/usr 用户程序 可选

常见文件系统类型

磁盘文件系统

1. ext2/ext3/ext4

  • 特点:Linux标准文件系统,功能完善
  • 适用:SD卡、硬盘、U盘
  • 优势:成熟稳定,工具完善
  • 劣势:不适合Flash存储
# 创建ext4文件系统
mkfs.ext4 /dev/mmcblk0p2

# 挂载
mount -t ext4 /dev/mmcblk0p2 /mnt

# 查看信息
tune2fs -l /dev/mmcblk0p2

2. FAT/FAT32/exFAT

  • 特点:简单通用,跨平台兼容
  • 适用:SD卡、U盘、数据交换
  • 优势:兼容性好,实现简单
  • 劣势:功能有限,不支持权限
# 创建FAT32文件系统
mkfs.vfat -F 32 /dev/mmcblk0p1

# 挂载
mount -t vfat /dev/mmcblk0p1 /mnt

Flash文件系统

1. JFFS2 (Journaling Flash File System 2)

  • 特点:专为NOR Flash设计
  • 适用:NOR Flash存储
  • 优势:支持压缩,掉电保护
  • 劣势:挂载慢,不适合大容量
# 创建JFFS2镜像
mkfs.jffs2 -r rootfs/ -o rootfs.jffs2 -e 0x20000 -p

# 挂载(需要MTD设备)
mount -t jffs2 /dev/mtdblock2 /mnt

2. UBIFS (Unsorted Block Image File System)

  • 特点:专为NAND Flash设计
  • 适用:NAND Flash存储
  • 优势:性能好,适合大容量
  • 劣势:配置复杂
# 创建UBIFS镜像
mkfs.ubifs -r rootfs/ -m 2048 -e 126976 -c 2048 -o rootfs.ubifs

# 创建UBI镜像
ubinize -o rootfs.ubi -m 2048 -p 128KiB ubinize.cfg

# 挂载
ubiattach /dev/ubi_ctrl -m 2
mount -t ubifs ubi0:rootfs /mnt

3. YAFFS2 (Yet Another Flash File System 2)

  • 特点:轻量级Flash文件系统
  • 适用:NAND Flash
  • 优势:简单高效
  • 劣势:不在主线内核中

内存文件系统

1. tmpfs

  • 特点:基于内存和swap的文件系统
  • 适用:临时文件、/tmp目录
  • 优势:速度快,动态调整大小
  • 劣势:掉电丢失
# 挂载tmpfs
mount -t tmpfs -o size=10M tmpfs /tmp

# 查看使用情况
df -h /tmp

2. ramfs

  • 特点:纯内存文件系统
  • 适用:initramfs
  • 优势:简单快速
  • 劣势:不限制大小,可能耗尽内存
# 挂载ramfs
mount -t ramfs ramfs /mnt

虚拟文件系统

1. proc

  • 特点:提供进程和内核信息
  • 挂载点:/proc
  • 用途:查看系统状态
# 挂载proc
mount -t proc proc /proc

# 查看CPU信息
cat /proc/cpuinfo

# 查看内存信息
cat /proc/meminfo

2. sysfs

  • 特点:提供设备和驱动信息
  • 挂载点:/sys
  • 用途:设备管理和配置
# 挂载sysfs
mount -t sysfs sysfs /sys

# 查看设备
ls /sys/class/
ls /sys/devices/

3. devtmpfs

  • 特点:自动管理设备节点
  • 挂载点:/dev
  • 用途:设备文件管理
# 挂载devtmpfs
mount -t devtmpfs devtmpfs /dev

文件系统对比

文件系统 存储介质 压缩 掉电保护 性能 适用场景
ext4 磁盘/SD卡 通用存储
FAT32 磁盘/SD卡 数据交换
JFFS2 NOR Flash 小容量Flash
UBIFS NAND Flash 大容量Flash
tmpfs 内存 极高 临时文件
squashfs 只读 - 只读根文件系统

BusyBox工具集

BusyBox简介

BusyBox是一个集成了数百个常用Linux命令的单一可执行文件,被称为"嵌入式Linux的瑞士军刀"。

特点: - 体积小:通常只有1-2MB - 功能全:包含300+常用命令 - 可配置:可以选择需要的功能 - 易移植:支持多种架构

包含的工具

文件操作:ls, cp, mv, rm, mkdir, cat, more, less
文本处理:grep, sed, awk, cut, sort, uniq
网络工具:ifconfig, route, ping, telnet, wget, ftpget
系统管理:init, mount, umount, ps, top, kill
Shell:ash (轻量级shell)

下载和配置BusyBox

1. 下载源码

# 下载BusyBox
cd ~/embedded
wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2

# 解压
tar -xjf busybox-1.35.0.tar.bz2
cd busybox-1.35.0

2. 配置BusyBox

# 使用默认配置
make defconfig

# 或使用图形化配置
make menuconfig

重要配置选项

Settings  --->
    [*] Build static binary (no shared libs)
    (/opt/arm-linux-gnueabihf) Cross compiler prefix

Coreutils  --->
    [*] ls
    [*] cp
    [*] mv
    [*] rm
    [*] mkdir

Editors  --->
    [*] vi

Init Utilities  --->
    [*] init
    [*] Support reading an inittab file

Networking Utilities  --->
    [*] ifconfig
    [*] ping
    [*] wget

Shells  --->
    [*] ash
    [*] bash-compatible extensions

3. 编译BusyBox

# 设置交叉编译工具链
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

# 编译
make -j$(nproc)

# 安装到指定目录
make CONFIG_PREFIX=/path/to/rootfs install

编译结果

# 查看生成的文件
ls -lh busybox
# -rwxr-xr-x 1 user user 1.2M Jan 15 10:00 busybox

# 查看包含的命令
./busybox --list

BusyBox安装结构

安装后的目录结构:

rootfs/
├── bin/
│   ├── busybox
│   ├── sh -> busybox
│   ├── ls -> busybox
│   ├── cp -> busybox
│   └── ... (其他命令的符号链接)
├── sbin/
│   ├── init -> ../bin/busybox
│   ├── mount -> ../bin/busybox
│   └── ... (系统命令的符号链接)
└── usr/
    ├── bin/
    │   └── ... (用户命令的符号链接)
    └── sbin/
        └── ... (用户系统命令的符号链接)

工作原理: - BusyBox是单一可执行文件 - 其他命令都是指向BusyBox的符号链接 - BusyBox根据调用名称执行相应功能

# 验证符号链接
ls -l bin/ls
# lrwxrwxrwx 1 root root 7 Jan 15 10:00 bin/ls -> busybox

# 直接调用
./bin/busybox ls

# 通过符号链接调用
./bin/ls

制作根文件系统

创建目录结构

1. 创建基本目录

# 设置根文件系统路径
export ROOTFS=~/embedded/rootfs
mkdir -p $ROOTFS

# 创建标准目录
cd $ROOTFS
mkdir -p bin sbin etc dev proc sys tmp root home
mkdir -p usr/bin usr/sbin usr/lib
mkdir -p var/log var/run
mkdir -p lib/modules

# 设置权限
chmod 1777 tmp
chmod 700 root

2. 安装BusyBox

# 编译并安装BusyBox
cd ~/embedded/busybox-1.35.0
make CONFIG_PREFIX=$ROOTFS install

# 验证安装
ls -l $ROOTFS/bin/busybox

3. 复制库文件

# 设置工具链路径
export TOOLCHAIN=/opt/arm-linux-gnueabihf

# 复制C库
cp -a $TOOLCHAIN/libc/lib/*.so* $ROOTFS/lib/

# 或只复制必需的库
arm-linux-gnueabihf-readelf -d $ROOTFS/bin/busybox | grep NEEDED
# 根据输出复制所需的库文件

# 复制动态链接器
cp -a $TOOLCHAIN/libc/lib/ld-linux-armhf.so.3 $ROOTFS/lib/

# 创建符号链接
cd $ROOTFS/lib
ln -s ld-linux-armhf.so.3 ld-linux.so.3

库文件精简

# 只复制必需的库(推荐)
mkdir -p $ROOTFS/lib

# 复制基本C库
cp $TOOLCHAIN/libc/lib/libc.so.6 $ROOTFS/lib/
cp $TOOLCHAIN/libc/lib/libm.so.6 $ROOTFS/lib/
cp $TOOLCHAIN/libc/lib/libdl.so.2 $ROOTFS/lib/
cp $TOOLCHAIN/libc/lib/libpthread.so.0 $ROOTFS/lib/
cp $TOOLCHAIN/libc/lib/libresolv.so.2 $ROOTFS/lib/
cp $TOOLCHAIN/libc/lib/ld-linux-armhf.so.3 $ROOTFS/lib/

# 去除调试符号(减小体积)
arm-linux-gnueabihf-strip $ROOTFS/lib/*.so*

创建设备文件

方法1:手动创建(静态设备节点)

cd $ROOTFS/dev

# 创建控制台设备
sudo mknod -m 666 console c 5 1
sudo mknod -m 666 null c 1 3
sudo mknod -m 666 zero c 1 5

# 创建串口设备
sudo mknod -m 666 ttyS0 c 4 64
sudo mknod -m 666 ttyS1 c 4 65

# 创建块设备
sudo mknod -m 660 mmcblk0 b 179 0
sudo mknod -m 660 mmcblk0p1 b 179 1
sudo mknod -m 660 mmcblk0p2 b 179 2

# 创建随机数设备
sudo mknod -m 644 random c 1 8
sudo mknod -m 644 urandom c 1 9

# 创建内存设备
sudo mknod -m 640 mem c 1 1
sudo mknod -m 666 kmem c 1 2

方法2:使用devtmpfs(推荐)

在内核配置中启用devtmpfs:

Device Drivers  --->
    Generic Driver Options  --->
        [*] Maintain a devtmpfs filesystem to mount at /dev
        [*]   Automount devtmpfs at /dev, after the kernel mounted the rootfs

在启动脚本中挂载:

mount -t devtmpfs devtmpfs /dev

方法3:使用mdev(BusyBox)

# 在/etc/init.d/rcS中添加
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

配置系统文件

1. 创建/etc/inittab

cat > $ROOTFS/etc/inittab << 'EOF'
# /etc/inittab

# 系统初始化
::sysinit:/etc/init.d/rcS

# 启动shell(串口控制台)
console::askfirst:-/bin/sh

# 系统重启和关机
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF

2. 创建启动脚本/etc/init.d/rcS

mkdir -p $ROOTFS/etc/init.d

cat > $ROOTFS/etc/init.d/rcS << 'EOF'
#!/bin/sh

# 挂载虚拟文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmpfs /tmp

# 创建必要的目录
mkdir -p /dev/pts /dev/shm
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm

# 设置主机名
hostname -F /etc/hostname

# 配置网络接口
ifconfig lo 127.0.0.1 up
ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up
route add default gw 192.168.1.1

# 启动系统日志
syslogd
klogd

# 显示启动信息
echo "System initialization complete"
echo "Welcome to Embedded Linux!"
EOF

# 设置执行权限
chmod +x $ROOTFS/etc/init.d/rcS

3. 创建/etc/fstab

cat > $ROOTFS/etc/fstab << 'EOF'
# <file system> <mount point> <type> <options> <dump> <pass>
proc            /proc         proc   defaults  0      0
sysfs           /sys          sysfs  defaults  0      0
devtmpfs        /dev          devtmpfs defaults 0     0
tmpfs           /tmp          tmpfs  defaults  0      0
tmpfs           /dev/shm      tmpfs  defaults  0      0
devpts          /dev/pts      devpts defaults  0      0
EOF

4. 创建/etc/hostname

echo "embedded-linux" > $ROOTFS/etc/hostname

5. 创建/etc/passwd和/etc/group

# 创建用户文件
cat > $ROOTFS/etc/passwd << 'EOF'
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/false
bin:x:2:2:bin:/bin:/bin/false
sys:x:3:3:sys:/dev:/bin/false
nobody:x:65534:65534:nobody:/nonexistent:/bin/false
EOF

# 创建组文件
cat > $ROOTFS/etc/group << 'EOF'
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
lp:x:7:
mail:x:8:
news:x:9:
uucp:x:10:
man:x:12:
proxy:x:13:
kmem:x:15:
dialout:x:20:
fax:x:21:
voice:x:22:
cdrom:x:24:
floppy:x:25:
tape:x:26:
sudo:x:27:
audio:x:29:
dip:x:30:
www-data:x:33:
backup:x:34:
operator:x:37:
list:x:38:
irc:x:39:
src:x:40:
gnats:x:41:
shadow:x:42:
utmp:x:43:
video:x:44:
sasl:x:45:
plugdev:x:46:
staff:x:50:
games:x:60:
users:x:100:
nogroup:x:65534:
EOF

6. 创建/etc/profile

cat > $ROOTFS/etc/profile << 'EOF'
# /etc/profile

# 设置PATH
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

# 设置提示符
export PS1='[\u@\h \W]\$ '

# 设置语言
export LANG=C

# 设置编辑器
export EDITOR=vi

# 显示欢迎信息
echo "Welcome to Embedded Linux System"
echo "Kernel: $(uname -r)"
echo "Hostname: $(hostname)"
EOF

添加自定义应用程序

1. 编译应用程序

# 示例:编译一个简单的应用
cat > hello.c << 'EOF'
#include <stdio.h>

int main(void) {
    printf("Hello from Embedded Linux!\n");
    return 0;
}
EOF

# 交叉编译
arm-linux-gnueabihf-gcc -o hello hello.c -static

# 或动态链接
arm-linux-gnueabihf-gcc -o hello hello.c

# 去除调试符号
arm-linux-gnueabihf-strip hello

2. 复制到根文件系统

# 复制应用程序
cp hello $ROOTFS/usr/bin/

# 设置权限
chmod +x $ROOTFS/usr/bin/hello

# 如果是动态链接,复制依赖库
arm-linux-gnueabihf-readelf -d hello | grep NEEDED
# 根据输出复制所需库文件

3. 添加启动脚本

# 创建应用启动脚本
cat > $ROOTFS/etc/init.d/S99myapp << 'EOF'
#!/bin/sh

case "$1" in
    start)
        echo "Starting myapp..."
        /usr/bin/hello &
        ;;
    stop)
        echo "Stopping myapp..."
        killall hello
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac

exit 0
EOF

# 设置执行权限
chmod +x $ROOTFS/etc/init.d/S99myapp

# 在rcS中调用
echo "/etc/init.d/S99myapp start" >> $ROOTFS/etc/init.d/rcS

制作文件系统镜像

方法1:制作ext4镜像

# 创建空镜像文件(100MB)
dd if=/dev/zero of=rootfs.ext4 bs=1M count=100

# 格式化为ext4
mkfs.ext4 rootfs.ext4

# 挂载镜像
mkdir -p /mnt/rootfs
sudo mount -o loop rootfs.ext4 /mnt/rootfs

# 复制文件系统内容
sudo cp -a $ROOTFS/* /mnt/rootfs/

# 卸载
sudo umount /mnt/rootfs

# 压缩镜像(可选)
gzip rootfs.ext4

方法2:制作JFFS2镜像

# 安装工具
sudo apt install mtd-utils

# 创建JFFS2镜像
# -r: 根文件系统目录
# -o: 输出文件
# -e: 擦除块大小(根据Flash规格)
# -p: 填充大小
mkfs.jffs2 -r $ROOTFS -o rootfs.jffs2 -e 0x20000 -p

# 查看镜像信息
ls -lh rootfs.jffs2

方法3:制作UBIFS镜像

# 创建UBIFS镜像
# -r: 根文件系统目录
# -m: 最小I/O单元大小(页大小)
# -e: 逻辑擦除块大小
# -c: 最大逻辑擦除块数量
mkfs.ubifs -r $ROOTFS -m 2048 -e 126976 -c 2048 -o rootfs.ubifs

# 创建ubinize配置文件
cat > ubinize.cfg << 'EOF'
[ubifs]
mode=ubi
image=rootfs.ubifs
vol_id=0
vol_size=50MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize
EOF

# 创建UBI镜像
ubinize -o rootfs.ubi -m 2048 -p 128KiB ubinize.cfg

方法4:制作initramfs

# 方法A:使用cpio
cd $ROOTFS
find . | cpio -o -H newc | gzip > ../initramfs.cpio.gz

# 方法B:使用内核脚本
cd ~/linux-5.15
scripts/gen_initramfs_list.sh -o initramfs.cpio.gz $ROOTFS

# 方法C:编译到内核
# 在内核配置中设置
General setup  --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    (/path/to/rootfs) Initramfs source file(s)

方法5:制作squashfs镜像(只读)

# 安装工具
sudo apt install squashfs-tools

# 创建squashfs镜像
mksquashfs $ROOTFS rootfs.squashfs -comp xz

# 查看镜像信息
unsquashfs -s rootfs.squashfs

启动和测试

使用QEMU测试

1. 安装QEMU

sudo apt install qemu-system-arm

2. 准备内核和根文件系统

# 假设已有编译好的内核
KERNEL=~/linux-5.15/arch/arm/boot/zImage
DTB=~/linux-5.15/arch/arm/boot/dts/vexpress-v2p-ca9.dtb
ROOTFS=~/embedded/rootfs.ext4

3. 启动QEMU

qemu-system-arm \
    -M vexpress-a9 \
    -m 512M \
    -kernel $KERNEL \
    -dtb $DTB \
    -drive file=$ROOTFS,format=raw,if=sd \
    -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
    -nographic

使用initramfs启动

qemu-system-arm \
    -M vexpress-a9 \
    -m 512M \
    -kernel $KERNEL \
    -dtb $DTB \
    -initrd initramfs.cpio.gz \
    -append "rdinit=/sbin/init console=ttyAMA0" \
    -nographic

在真实硬件上测试

1. 准备SD卡

# 查看SD卡设备
lsblk

# 分区(假设SD卡为/dev/sdb)
sudo fdisk /dev/sdb

# 创建两个分区:
# 分区1:FAT32,100MB,用于boot
# 分区2:ext4,剩余空间,用于rootfs

# 格式化分区
sudo mkfs.vfat -F 32 /dev/sdb1
sudo mkfs.ext4 /dev/sdb2

# 挂载分区
sudo mkdir -p /mnt/boot /mnt/rootfs
sudo mount /dev/sdb1 /mnt/boot
sudo mount /dev/sdb2 /mnt/rootfs

2. 复制文件

# 复制内核和设备树到boot分区
sudo cp zImage /mnt/boot/
sudo cp imx6ull-myboard.dtb /mnt/boot/

# 复制根文件系统到rootfs分区
sudo cp -a $ROOTFS/* /mnt/rootfs/

# 同步并卸载
sync
sudo umount /mnt/boot /mnt/rootfs

3. 配置U-Boot

# 在U-Boot命令行中设置启动参数
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
setenv bootcmd 'fatload mmc 1:1 0x80800000 zImage; fatload mmc 1:1 0x83000000 imx6ull-myboard.dtb; bootz 0x80800000 - 0x83000000'
saveenv

# 启动系统
boot

验证系统功能

1. 检查启动日志

# 查看内核启动信息
dmesg | less

# 查看挂载的文件系统
mount

# 查看进程
ps aux

# 查看内存使用
free -h

# 查看磁盘使用
df -h

2. 测试基本命令

# 文件操作
ls -la /
cd /tmp
touch test.txt
echo "Hello" > test.txt
cat test.txt
rm test.txt

# 网络测试
ifconfig
ping -c 3 192.168.1.1

# 系统信息
uname -a
cat /proc/cpuinfo
cat /proc/meminfo

3. 测试设备

# 查看设备
ls -l /dev/

# 测试串口
echo "test" > /dev/ttyS0

# 测试GPIO(如果有驱动)
echo 1 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio1/direction
echo 1 > /sys/class/gpio/gpio1/value

系统优化

减小文件系统大小

1. 精简BusyBox

# 重新配置BusyBox,只选择必需的命令
cd ~/embedded/busybox-1.35.0
make menuconfig

# 禁用不需要的功能
# - 禁用不常用的命令
# - 禁用调试功能
# - 使用静态编译

# 重新编译
make clean
make -j$(nproc)
make CONFIG_PREFIX=$ROOTFS install

2. 精简库文件

# 只复制必需的库
# 使用ldd或readelf查看依赖
arm-linux-gnueabihf-readelf -d $ROOTFS/bin/busybox | grep NEEDED

# 去除调试符号
find $ROOTFS -name "*.so*" -exec arm-linux-gnueabihf-strip {} \;
find $ROOTFS -type f -executable -exec arm-linux-gnueabihf-strip {} \; 2>/dev/null

3. 删除不必要的文件

# 删除文档和手册
rm -rf $ROOTFS/usr/share/doc
rm -rf $ROOTFS/usr/share/man
rm -rf $ROOTFS/usr/share/info

# 删除开发文件
rm -rf $ROOTFS/usr/include
rm -rf $ROOTFS/usr/lib/*.a

# 删除临时文件
find $ROOTFS -name "*.o" -delete
find $ROOTFS -name "*.a" -delete

4. 使用压缩文件系统

# 使用squashfs(只读,高压缩比)
mksquashfs $ROOTFS rootfs.squashfs -comp xz -b 256K

# 使用JFFS2(读写,支持压缩)
mkfs.jffs2 -r $ROOTFS -o rootfs.jffs2 -e 0x20000 -p -n

# 对比大小
ls -lh rootfs.*

优化启动速度

1. 精简启动脚本

# 优化/etc/init.d/rcS
cat > $ROOTFS/etc/init.d/rcS << 'EOF'
#!/bin/sh

# 只挂载必需的文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

# 延迟挂载不紧急的文件系统
(
    sleep 1
    mount -t tmpfs tmpfs /tmp
    mkdir -p /dev/pts /dev/shm
    mount -t devpts devpts /dev/pts
    mount -t tmpfs tmpfs /dev/shm
) &

# 快速配置网络
ifconfig lo 127.0.0.1 up &
ifconfig eth0 192.168.1.100 up &

echo "System ready"
EOF

2. 使用并行启动

# 在启动脚本中使用后台执行
service1 &
service2 &
service3 &
wait  # 等待所有后台任务完成

3. 禁用不必要的服务

# 注释掉不需要的服务
# syslogd
# klogd
# 不需要的网络服务

4. 优化内核启动参数

# 减少启动延迟
quiet  # 减少内核日志输出
lpj=xxxxx  # 预设loops_per_jiffy,跳过校准

优化运行性能

1. 使用合适的文件系统

# 对于只读系统,使用squashfs
# 对于读写系统,使用ext4或ubifs
# 对于临时文件,使用tmpfs

2. 调整内存分配

# 在/etc/sysctl.conf中配置
vm.swappiness = 10  # 减少swap使用
vm.dirty_ratio = 10  # 控制脏页比例
vm.dirty_background_ratio = 5

3. 优化I/O调度

# 设置I/O调度器
echo deadline > /sys/block/mmcblk0/queue/scheduler

# 调整读写缓存
echo 128 > /sys/block/mmcblk0/queue/read_ahead_kb

安全加固

1. 设置root密码

# 在开发主机上生成密码哈希
openssl passwd -1 "your_password"
# 输出:$1$xxxxx$yyyyy

# 修改/etc/shadow
cat > $ROOTFS/etc/shadow << 'EOF'
root:$1$xxxxx$yyyyy:18000:0:99999:7:::
daemon:*:18000:0:99999:7:::
bin:*:18000:0:99999:7:::
sys:*:18000:0:99999:7:::
nobody:*:18000:0:99999:7:::
EOF

chmod 640 $ROOTFS/etc/shadow

2. 限制网络服务

# 禁用telnet,使用dropbear SSH
# 配置防火墙规则
# 限制root远程登录

3. 文件权限设置

# 设置关键文件权限
chmod 600 $ROOTFS/etc/shadow
chmod 644 $ROOTFS/etc/passwd
chmod 644 $ROOTFS/etc/group
chmod 755 $ROOTFS/etc/init.d/*

故障排除

常见启动问题

问题1:内核panic - not syncing: VFS: Unable to mount root fs

原因: - 根文件系统路径错误 - 文件系统类型不支持 - 设备驱动未加载

解决方法

# 检查内核启动参数
# 确保root=参数正确
root=/dev/mmcblk0p2  # 或其他正确的设备

# 检查文件系统支持
# 在内核配置中启用相应的文件系统
File systems  --->
    <*> Second extended fs support
    <*> The Extended 4 (ext4) filesystem

# 检查设备驱动
# 确保SD卡或Flash驱动已编译到内核

问题2:Warning: unable to open an initial console

原因: - /dev/console设备节点不存在 - 设备权限错误

解决方法

# 创建console设备节点
cd $ROOTFS/dev
sudo mknod -m 666 console c 5 1

# 或在内核中启用devtmpfs
Device Drivers  --->
    Generic Driver Options  --->
        [*] Maintain a devtmpfs filesystem to mount at /dev
        [*]   Automount devtmpfs at /dev

问题3:init: can't open /dev/ttyXXX: No such file or directory

原因: - 串口设备节点不存在 - inittab中的设备名称错误

解决方法

# 创建串口设备节点
cd $ROOTFS/dev
sudo mknod -m 666 ttyS0 c 4 64  # 或ttyAMA0等

# 检查inittab配置
# 确保设备名称与实际设备匹配
console::askfirst:-/bin/sh
# 或
ttyS0::askfirst:-/bin/sh

问题4:/bin/sh: can't access tty; job control turned off

原因: - 终端设备配置问题 - 权限问题

解决方法

# 修改inittab,使用askfirst而不是respawn
console::askfirst:-/bin/sh

# 确保设备权限正确
chmod 666 $ROOTFS/dev/console

调试技巧

1. 启用内核调试输出

# 在内核启动参数中添加
console=ttyS0,115200 debug loglevel=8

# 或在运行时调整
echo 8 > /proc/sys/kernel/printk

2. 使用initramfs调试

# 创建调试用的initramfs
cat > init << 'EOF'
#!/bin/sh

mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

echo "=== Debug Shell ==="
echo "Press Ctrl+D to continue boot"
/bin/sh

# 继续正常启动
exec switch_root /mnt/rootfs /sbin/init
EOF

chmod +x init
find . | cpio -o -H newc | gzip > initramfs-debug.cpio.gz

3. 检查文件系统完整性

# 检查ext4文件系统
e2fsck -f rootfs.ext4

# 检查并修复
e2fsck -p rootfs.ext4

# 查看文件系统信息
tune2fs -l rootfs.ext4

4. 使用串口调试

# 连接串口
sudo minicom -D /dev/ttyUSB0 -b 115200

# 或使用screen
sudo screen /dev/ttyUSB0 115200

# 查看启动日志
# 所有内核和init的输出都会显示在串口

5. 分析启动时间

# 在内核启动参数中添加
initcall_debug

# 查看启动时间
dmesg | grep "initcall"

# 使用systemd-analyze(如果使用systemd)
systemd-analyze
systemd-analyze blame

性能分析

1. 查看系统资源使用

# CPU使用率
top
# 或
mpstat 1

# 内存使用
free -h
cat /proc/meminfo

# 磁盘I/O
iostat -x 1

# 进程资源
ps aux --sort=-%mem | head
ps aux --sort=-%cpu | head

2. 分析启动过程

# 记录启动时间
cat /proc/uptime

# 查看服务启动时间
systemd-analyze blame  # 如果使用systemd

# 手动计时
# 在启动脚本中添加
echo "Start: $(date +%s.%N)" > /tmp/boot.log
# ... 执行操作 ...
echo "End: $(date +%s.%N)" >> /tmp/boot.log

3. 内存泄漏检测

# 监控内存使用
watch -n 1 free -h

# 查看进程内存
cat /proc/<pid>/status | grep Vm

# 使用valgrind(需要交叉编译)
valgrind --leak-check=full ./your_app

高级主题

使用Buildroot

Buildroot是一个自动化构建嵌入式Linux系统的工具。

1. 下载和配置

# 下载Buildroot
git clone https://github.com/buildroot/buildroot.git
cd buildroot

# 查看可用的配置
make list-defconfigs

# 使用预定义配置
make raspberrypi3_defconfig

# 或自定义配置
make menuconfig

2. 配置选项

Target options  --->
    Target Architecture (ARM (little endian))
    Target Architecture Variant (cortex-A7)

Toolchain  --->
    Toolchain type (External toolchain)

System configuration  --->
    System hostname (embedded-linux)
    Root password (设置密码)

Filesystem images  --->
    [*] ext2/3/4 root filesystem
    [*] tar the root filesystem

3. 编译

# 开始编译(需要较长时间)
make -j$(nproc)

# 输出文件在output/images/目录
ls output/images/
# rootfs.ext4
# zImage
# *.dtb

使用Yocto Project

Yocto是更强大的嵌入式Linux构建系统。

1. 安装依赖

sudo apt install gawk wget git diffstat unzip texinfo gcc build-essential \
    chrpath socat cpio python3 python3-pip python3-pexpect xz-utils \
    debianutils iputils-ping python3-git python3-jinja2 libegl1-mesa \
    libsdl1.2-dev pylint3 xterm python3-subunit mesa-common-dev zstd liblz4-tool

2. 下载Poky

git clone git://git.yoctoproject.org/poky
cd poky
git checkout -b dunfell origin/dunfell

3. 初始化环境

source oe-init-build-env

4. 配置和构建

# 编辑conf/local.conf
MACHINE = "qemuarm"
DISTRO = "poky"

# 构建最小镜像
bitbake core-image-minimal

# 或构建完整镜像
bitbake core-image-sato

只读根文件系统

优势: - 防止文件系统损坏 - 提高系统稳定性 - 适合工业应用

实现方法

1. 使用squashfs

# 创建只读根文件系统
mksquashfs $ROOTFS rootfs.squashfs -comp xz

# 挂载为只读
mount -t squashfs -o loop,ro rootfs.squashfs /mnt

2. 配置可写分区

# 创建overlay文件系统
mount -t tmpfs tmpfs /tmp
mount -t tmpfs tmpfs /var

# 或使用overlayfs
mount -t overlay overlay \
    -o lowerdir=/ro-root,upperdir=/rw-root,workdir=/work \
    /root

3. 修改fstab

cat > /etc/fstab << 'EOF'
# 只读根文件系统
/dev/mmcblk0p2  /           squashfs  ro              0  0

# 可写分区
tmpfs           /tmp        tmpfs     defaults        0  0
tmpfs           /var        tmpfs     defaults        0  0
/dev/mmcblk0p3  /data       ext4      defaults        0  2
EOF

网络文件系统 (NFS)

用途: - 开发调试 - 快速测试 - 共享文件

服务器端配置

# 安装NFS服务器
sudo apt install nfs-kernel-server

# 配置导出目录
sudo vi /etc/exports
# 添加:
/path/to/rootfs 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check)

# 重启NFS服务
sudo exportfs -ra
sudo systemctl restart nfs-kernel-server

客户端配置

# 内核启动参数
root=/dev/nfs nfsroot=192.168.1.100:/path/to/rootfs,v3,tcp ip=dhcp

# 或手动挂载
mount -t nfs -o nolock 192.168.1.100:/path/to/rootfs /mnt

多分区方案

典型分区布局

/dev/mmcblk0
├── /dev/mmcblk0p1  # boot分区 (FAT32, 100MB)
│   ├── zImage
│   ├── *.dtb
│   └── boot.scr
├── /dev/mmcblk0p2  # rootfs分区 (ext4/squashfs, 500MB)
│   └── 根文件系统
├── /dev/mmcblk0p3  # data分区 (ext4, 剩余空间)
│   └── 用户数据
└── /dev/mmcblk0p4  # 备份分区 (可选)
    └── 备份根文件系统

fstab配置

cat > /etc/fstab << 'EOF'
/dev/mmcblk0p1  /boot       vfat      defaults        0  2
/dev/mmcblk0p2  /           ext4      defaults        0  1
/dev/mmcblk0p3  /data       ext4      defaults        0  2
tmpfs           /tmp        tmpfs     defaults        0  0
EOF

实战项目:完整根文件系统制作

项目目标

制作一个功能完整的嵌入式Linux根文件系统,包含: - 基本系统工具(BusyBox) - 网络功能(静态IP和DHCP) - SSH服务(Dropbear) - 自定义应用程序 - 系统监控工具

项目步骤

步骤1:准备工作环境

# 创建工作目录
mkdir -p ~/embedded-project
cd ~/embedded-project

# 设置环境变量
export ROOTFS=$PWD/rootfs
export TOOLCHAIN=/opt/arm-linux-gnueabihf
export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm

步骤2:编译BusyBox

# 下载BusyBox
wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
tar -xjf busybox-1.35.0.tar.bz2
cd busybox-1.35.0

# 配置
make defconfig
make menuconfig
# Settings -> Build static binary: YES
# Settings -> Cross compiler prefix: arm-linux-gnueabihf-

# 编译和安装
make -j$(nproc)
make CONFIG_PREFIX=$ROOTFS install
cd ..

步骤3:创建目录结构

cd $ROOTFS
mkdir -p etc/init.d etc/network dev proc sys tmp root home
mkdir -p usr/lib var/log var/run lib/modules
chmod 1777 tmp
chmod 700 root

步骤4:复制库文件

# 复制必需的库
cp -a $TOOLCHAIN/libc/lib/libc.so.6 lib/
cp -a $TOOLCHAIN/libc/lib/libm.so.6 lib/
cp -a $TOOLCHAIN/libc/lib/libdl.so.2 lib/
cp -a $TOOLCHAIN/libc/lib/libpthread.so.0 lib/
cp -a $TOOLCHAIN/libc/lib/libresolv.so.2 lib/
cp -a $TOOLCHAIN/libc/lib/ld-linux-armhf.so.3 lib/

# 去除调试符号
$CROSS_COMPILE-strip lib/*.so*

步骤5:配置系统文件

# 创建inittab
cat > etc/inittab << 'EOF'
::sysinit:/etc/init.d/rcS
console::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF

# 创建rcS
cat > etc/init.d/rcS << 'EOF'
#!/bin/sh

echo "Starting system..."

# 挂载文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmpfs /tmp
mkdir -p /dev/pts /dev/shm
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm

# 设置主机名
hostname -F /etc/hostname

# 配置网络
/etc/init.d/S40network start

# 启动SSH
/etc/init.d/S50dropbear start

# 启动自定义应用
/etc/init.d/S99myapp start

echo "System initialization complete"
EOF

chmod +x etc/init.d/rcS

# 创建hostname
echo "embedded-linux" > etc/hostname

# 创建passwd和group
cat > etc/passwd << 'EOF'
root:x:0:0:root:/root:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/false
EOF

cat > etc/group << 'EOF'
root:x:0:
nogroup:x:65534:
EOF

# 创建fstab
cat > etc/fstab << 'EOF'
proc            /proc         proc   defaults  0  0
sysfs           /sys          sysfs  defaults  0  0
devtmpfs        /dev          devtmpfs defaults 0 0
tmpfs           /tmp          tmpfs  defaults  0  0
EOF

# 创建profile
cat > etc/profile << 'EOF'
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PS1='[\u@\h \W]\$ '
export LANG=C
echo "Welcome to Embedded Linux System"
EOF

步骤6:配置网络

# 创建网络配置目录
mkdir -p etc/network

# 创建interfaces文件
cat > etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 192.168.1.100
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 8.8.8.8
EOF

# 创建网络启动脚本
cat > etc/init.d/S40network << 'EOF'
#!/bin/sh

case "$1" in
    start)
        echo "Configuring network..."
        ifconfig lo 127.0.0.1 up
        ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up
        route add default gw 192.168.1.1
        echo "nameserver 8.8.8.8" > /etc/resolv.conf
        ;;
    stop)
        echo "Stopping network..."
        ifconfig eth0 down
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac
EOF

chmod +x etc/init.d/S40network

步骤7:添加Dropbear SSH

# 下载Dropbear
cd ~/embedded-project
wget https://matt.ucc.asn.au/dropbear/releases/dropbear-2022.83.tar.bz2
tar -xjf dropbear-2022.83.tar.bz2
cd dropbear-2022.83

# 配置和编译
./configure --host=arm-linux-gnueabihf \
    --prefix=/usr \
    --disable-zlib
make PROGRAMS="dropbear dbclient dropbearkey scp"
make DESTDIR=$ROOTFS install

# 去除调试符号
$CROSS_COMPILE-strip $ROOTFS/usr/sbin/dropbear
$CROSS_COMPILE-strip $ROOTFS/usr/bin/dbclient
$CROSS_COMPILE-strip $ROOTFS/usr/bin/dropbearkey
$CROSS_COMPILE-strip $ROOTFS/usr/bin/scp

# 创建SSH目录
mkdir -p $ROOTFS/etc/dropbear

# 生成主机密钥(在目标系统首次启动时)
# dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
# dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key

# 创建Dropbear启动脚本
cat > $ROOTFS/etc/init.d/S50dropbear << 'EOF'
#!/bin/sh

DROPBEAR=/usr/sbin/dropbear
DROPBEAR_KEY_DIR=/etc/dropbear

case "$1" in
    start)
        echo "Starting dropbear sshd..."

        # 生成主机密钥(如果不存在)
        if [ ! -f $DROPBEAR_KEY_DIR/dropbear_rsa_host_key ]; then
            echo "Generating RSA host key..."
            dropbearkey -t rsa -f $DROPBEAR_KEY_DIR/dropbear_rsa_host_key
        fi

        # 启动dropbear
        $DROPBEAR -R
        ;;
    stop)
        echo "Stopping dropbear sshd..."
        killall dropbear
        ;;
    restart)
        $0 stop
        sleep 1
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac
EOF

chmod +x $ROOTFS/etc/init.d/S50dropbear

cd ~/embedded-project

步骤8:添加自定义应用

# 创建示例应用
cat > myapp.c << 'EOF'
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

volatile int running = 1;

void signal_handler(int sig) {
    running = 0;
}

int main(void) {
    signal(SIGTERM, signal_handler);
    signal(SIGINT, signal_handler);

    printf("MyApp started\n");

    while (running) {
        printf("MyApp is running...\n");
        sleep(10);
    }

    printf("MyApp stopped\n");
    return 0;
}
EOF

# 编译
$CROSS_COMPILE-gcc -o myapp myapp.c
$CROSS_COMPILE-strip myapp

# 安装
cp myapp $ROOTFS/usr/bin/
chmod +x $ROOTFS/usr/bin/myapp

# 创建启动脚本
cat > $ROOTFS/etc/init.d/S99myapp << 'EOF'
#!/bin/sh

DAEMON=/usr/bin/myapp
PIDFILE=/var/run/myapp.pid

case "$1" in
    start)
        echo "Starting myapp..."
        start-stop-daemon -S -b -m -p $PIDFILE -x $DAEMON
        ;;
    stop)
        echo "Stopping myapp..."
        start-stop-daemon -K -p $PIDFILE
        rm -f $PIDFILE
        ;;
    restart)
        $0 stop
        sleep 1
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac
EOF

chmod +x $ROOTFS/etc/init.d/S99myapp

步骤9:创建设备节点

cd $ROOTFS/dev
sudo mknod -m 666 console c 5 1
sudo mknod -m 666 null c 1 3
sudo mknod -m 666 zero c 1 5
sudo mknod -m 666 ttyS0 c 4 64
sudo mknod -m 644 random c 1 8
sudo mknod -m 644 urandom c 1 9
cd ~/embedded-project

步骤10:制作文件系统镜像

# 创建ext4镜像
dd if=/dev/zero of=rootfs.ext4 bs=1M count=200
mkfs.ext4 rootfs.ext4

# 挂载并复制文件
mkdir -p /tmp/rootfs-mount
sudo mount -o loop rootfs.ext4 /tmp/rootfs-mount
sudo cp -a $ROOTFS/* /tmp/rootfs-mount/
sudo umount /tmp/rootfs-mount

# 压缩镜像
gzip rootfs.ext4

echo "Root filesystem created: rootfs.ext4.gz"
ls -lh rootfs.ext4.gz

项目测试

# 使用QEMU测试
qemu-system-arm \
    -M vexpress-a9 \
    -m 512M \
    -kernel zImage \
    -dtb vexpress-v2p-ca9.dtb \
    -drive file=rootfs.ext4,format=raw,if=sd \
    -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
    -nographic

# 测试网络
ping 192.168.1.1

# 测试SSH(从另一台机器)
ssh root@192.168.1.100

# 测试自定义应用
ps | grep myapp

总结

关键要点

通过本教程,你已经学习了:

  1. 文件系统基础
  2. Linux文件系统层次结构(FHS)
  3. 常见文件系统类型及其特点
  4. 磁盘、Flash和内存文件系统的选择

  5. BusyBox工具集

  6. BusyBox的配置和编译
  7. 符号链接的工作原理
  8. 功能裁剪和优化

  9. 根文件系统制作

  10. 目录结构创建
  11. 库文件复制和精简
  12. 设备节点管理
  13. 系统配置文件编写

  14. 启动配置

  15. init进程和inittab
  16. 启动脚本编写
  17. 网络配置
  18. 服务管理

  19. 系统优化

  20. 文件系统大小优化
  21. 启动速度优化
  22. 运行性能优化
  23. 安全加固

  24. 故障排除

  25. 常见启动问题
  26. 调试技巧
  27. 性能分析

最佳实践

1. 开发阶段 - 使用NFS根文件系统,便于快速测试 - 保持详细的构建日志 - 使用版本控制管理配置文件

2. 生产阶段 - 使用只读根文件系统提高稳定性 - 实施安全加固措施 - 优化启动时间和资源使用

3. 维护阶段 - 建立OTA升级机制 - 保留备份分区 - 实施日志管理策略

下一步学习

掌握了根文件系统制作后,你可以继续学习:

  1. 进程和线程编程
  2. 多进程应用开发
  3. 进程间通信(IPC)
  4. 线程同步机制

  5. 系统调用和应用开发

  6. Linux系统调用接口
  7. 文件I/O操作
  8. 网络编程

  9. 完整系统构建

  10. Bootloader配置
  11. 内核定制
  12. 驱动开发
  13. 应用程序集成

  14. 高级主题

  15. 容器技术(Docker)
  16. 实时性优化
  17. 安全启动
  18. OTA升级方案

常见问题

Q1: 根文件系统最小需要多大?

A: 最小的根文件系统可以小于10MB,包含: - BusyBox(静态编译):约1-2MB - 必需的库文件:约2-3MB - 配置文件和脚本:约1MB - 设备节点和目录:约1MB

实际大小取决于: - 选择的命令和功能 - 是否静态编译 - 是否包含额外应用 - 是否使用压缩文件系统

Q2: 静态编译和动态链接如何选择?

A: 选择依据:

静态编译: - 优点:不需要库文件,部署简单 - 缺点:文件大,不能共享库 - 适用:单一应用,资源充足

动态链接: - 优点:文件小,共享库,节省内存 - 缺点:需要管理库依赖 - 适用:多个应用,资源受限

推荐:嵌入式系统通常使用动态链接,但精简库文件。

Q3: 如何减小根文件系统大小?

A: 优化方法:

  1. 精简BusyBox:只选择必需的命令
  2. 去除调试符号:使用strip工具
  3. 精简库文件:只复制必需的库
  4. 使用压缩文件系统:squashfs、JFFS2
  5. 删除不必要文件:文档、头文件、静态库
  6. 优化配置文件:删除注释和空行

Q4: initramfs和initrd有什么区别?

A: 主要区别:

特性 initramfs initrd
格式 cpio归档 块设备镜像
挂载 直接解压到内存 需要挂载
大小 动态调整 固定大小
性能 更快 较慢
推荐 否(已过时)

现代系统推荐使用initramfs。

Q5: 如何实现根文件系统的OTA升级?

A: 常见方案:

方案1:双分区方案 - 保留两个根文件系统分区 - 升级时写入备用分区 - 重启时切换到新分区 - 失败时回滚到旧分区

方案2:差分升级 - 只传输变化的部分 - 使用rsync或bsdiff - 节省带宽和时间

方案3:容器化升级 - 使用Docker容器 - 只升级应用层 - 保持系统层稳定

Q6: 如何调试根文件系统启动失败?

A: 调试步骤:

  1. 启用详细日志

    console=ttyS0,115200 debug loglevel=8
    

  2. 使用initramfs调试

  3. 创建包含shell的initramfs
  4. 在启动过程中进入shell
  5. 手动执行挂载和初始化

  6. 检查设备节点

  7. 确保/dev/console存在
  8. 确保串口设备节点正确

  9. 验证文件系统

  10. 检查文件系统类型
  11. 验证文件系统完整性
  12. 确认挂载参数正确

  13. 分析内核日志

  14. 查看panic信息
  15. 检查设备驱动加载
  16. 确认根设备识别

参考资料

官方文档

工具和资源

推荐阅读

  • 《Embedded Linux Primer》 - Christopher Hallinan
  • 《Building Embedded Linux Systems》 - Karim Yaghmour
  • 《Mastering Embedded Linux Programming》 - Chris Simmonds
  • eLinux.org - 嵌入式Linux Wiki

在线资源


文档版本: 1.0
最后更新: 2024-01-15
作者: 嵌入式知识平台

相关教程: - Linux内核裁剪与配置 - 设备树详解 - Linux驱动开发入门 - Linux进程与线程编程