嵌入式 Linux 系统移植与 SD 卡量产镜像制作

目录

[1. 项目目标](#1. 项目目标)

[2. 整个系统启动流程理解](#2. 整个系统启动流程理解)

[3. 整体流程概览(五步走)](#3. 整体流程概览(五步走))

[3.1 存储分区规划(以32GB SD卡为例)](#3.1 存储分区规划(以32GB SD卡为例))

[3.2 Uboot启动流程设计(上电自动运行)](#3.2 Uboot启动流程设计(上电自动运行))

[3.3 修改Uboot源码并编译](#3.3 修改Uboot源码并编译)

[3.4 制作完整镜像文件(.img)](#3.4 制作完整镜像文件(.img))

[3.5 烧录到目标SD卡(工厂操作)](#3.5 烧录到目标SD卡(工厂操作))

[3.6 启动后的额外挂载(自动化脚本)](#3.6 启动后的额外挂载(自动化脚本))


1. 项目目标

将已经调试好的嵌入式 Linux 系统(Uboot + Linux Kernel + Device Tree + Rootfs)制作成一个完整 SD 卡镜像文件,交付给工厂进行批量烧写。

最终产物:imx6ull.img

工厂拿到这个文件后:

bash 复制代码
SD卡

↓

烧写 imx6ull.img

↓

上电

↓

自动启动Linux系统

2. 整个系统启动流程理解

一个嵌入式 Linux 开机过程:

bash 复制代码
上电

|

BootROM:

| 读取SD卡中的Uboot

Uboot启动:

| 加载Linux内核

| 加载设备树DTB

| 设置启动参数bootargs

Linux Kernel启动:

| 挂载Rootfs

用户空间启动:
|
Linux系统运行

3. 整体流程概览(五步走)

  1. 规划存储布局 -- 决定SD卡上每个分区放什么,多大。

  2. 配置Uboot启动参数 -- 让上电后自动加载内核和文件系统。

  3. 编译并烧写新的Uboot -- 把配置写进代码,生成新的Uboot二进制。

  4. 打包所有文件成一个镜像 -- 将Uboot、内核、设备树、根文件系统按分区布局打包成单一文件。

  5. 烧录与验证 -- 用工具把镜像烧进SD卡,上电测试。

3.1 存储分区规划(以32GB SD卡为例)

分区 大小 格式 存放内容
第1分区(FAT) ~50MB FAT32 Uboot二进制(u-boot.imx)、内核镜像(zImage)、设备树(.dtb)、可选的initrd(ramdisk.gz
第2分区(EXT4) ~150MB EXT4 根文件系统(即完整的Linux系统文件,如/bin, /etc, /lib, /usr等)
剩余空间 未分配或后续使用 --- 留给用户数据或其他分区(忽略)

为什么这么分?

  • 第1分区用FAT,因为Uboot可以轻松读取FAT分区来加载内核和设备树。

  • 第2分区用EXT4作为根文件系统,稳定且支持读写。

3.2 Uboot启动流程设计(上电自动运行)

Uboot(引导加载程序)启动后,需要自动完成三件事:

  1. 设置启动参数(bootargs) -- 告诉内核根文件系统在哪里、控制台串口参数等。

  2. 从SD卡第1分区加载内核(zImage)和设备树(.dtb)到内存

  3. 启动内核(bootz命令) ,内核随后挂载第2分区作为根文件系统。

关键环境变量解释(新手勿怕,只需知道它们控制启动行为):

  • bootdelay -- 倒计时秒数,超时后自动执行bootcmd中的命令。

  • bootcmd -- 倒计时结束后自动执行的命令。

  • bootargs -- 传递给内核的启动参数(例如 root 路径、控制台、文件系统类型)。

  • load -- 从存储设备加载文件到内存指定地址。

  • bootz -- 启动ARM Linux内核的命令。

  • run :负责执行一个环境变量 里预先写好的命令脚本 (它可以包含一条或多条 loadsetenvbootz 等命令)。

设计的启动命令序列(简化版)

bash 复制代码
1. 设置 bootargs:root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc console=ttymxc0,115200
2. 加载内核:load mmc 0:1 0x80800000 zImage
3. 加载设备树:load mmc 0:1 0x83000000 imx6ull-mmc-npi.dtb
4. 启动内核:bootz 0x80800000 - 0x83000000

注:mmc 0:1 表示第0个MMC设备(即SD卡)的第1个分区。

3.3 修改Uboot源码并编译

我们需要把上述启动命令固化到Uboot源码中,这样生成的Uboot二进制自带这些配置。

在Uboot源码目录下,找到板级配置文件(例如 include/configs/mx6ullfire.h),添加或修改以下宏定义:

bash 复制代码
"bootdelay=2\0" \
"args_sd_boot=setenv bootargs console=ttymxc0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext4 init=/linuxrc\0" \
"bootcmd_mmcx=load mmc 0:1 0x80800000 zImage;load mmc 0:1 0x83000000 imx6ull-mmc-npi.dtb;run args_sd_boot;bootz 0x80800000 - 0x83000000;\0" \
"bootcmd=run bootcmd_mmcx\0"

然后重新编译Uboot,得到新的 u-boot.imx 文件。

3.4 制作完整镜像文件(.img)

将以上所有文件按分区布局打包成一个镜像文件(例如 imx6ull.img)。

常用工具是 dd 配合分区表(如使用 fdisksgdisk 创建分区,再逐个写入数据)。

简化制作流程(假设你有一张已分区的SD卡,并已将各文件拷入对应分区):

将该脚本设为可执行,并添加到启动项即可,烧录完成后,将SD卡插入开发板,上电即可自动启动系统。


3.6 启动后的额外挂载(自动化脚本)

为了方便后期更新内核和调试,可以在根文件系统中加入一个开机自启动脚本(如 /etc/init.d/auto_mount.sh),自动挂载NFS共享目录和SD卡的第1分区(FAT分区),以便拷贝新内核或设备树。

示例脚本内容:

bash 复制代码
#!/bin/sh
# 设置IP(如果需要)
ifconfig eth0 192.168.10.50
# 挂载NFS共享目录(开发机共享文件夹)
mount -t nfs -o nolock 192.168.10.100:/home/rocky/ebf/shared /opt
# 挂载SD卡第一个分区(FAT)到/mnt,方便更新内核
mount -t vfat /dev/mmcblk0p1 /mnt
  • 使用 dd 将整个SD卡(包括分区表)导出为镜像文件:这样得到的 imx6ull.img 就是完整的量产镜像。

    bash 复制代码
    sudo dd if=/dev/sdb of=imx6ull.img bs=4M status=progress

    3.5 烧录到目标SD卡(工厂操作)

    工厂会使用两种方式之一:

  • 使用 dd 命令(在Linux烧录机):

    bash 复制代码
    sudo dd if=imx6ull.img of=/dev/sdX bs=4M status=progress oflag=sync

    其中 /dev/sdX 是SD卡设备(注意不要选错)。

  • 使用专用烧录工具 (如Win32DiskImager或厂家提供的工具),直接选择 .img 文件烧录。

3.6 shell脚本参考

一定要注意是自己的loop 不一定是loop0

bash 复制代码
# 创建两个目录用于挂载镜像的分区
mkdir -p sd/boot sd/root

# 生成一个 200MB 的空白镜像文件(bs=512, count=409600 => 200MiB)
# 其中约 50MB 给 boot 分区,150MB 给 rootfs 分区
dd if=/dev/zero of=./imx6ull.img bs=512 count=409600

# 在镜像上创建 MS-DOS(MBR)分区表
sudo parted imx6ull.img --script -- mklabel msdos

# 创建第一个分区(FAT32),用于 /boot
# 起始于 2048 扇区(为了对齐,2048*512=1MB),结束于 104447 扇区
# 大小约为 (104447 - 2048 + 1) * 512 ≈ 50MB
sudo parted imx6ull.img --script -- mkpart primary fat32 2048s 104447s

# 创建第二个分区(ext4),用于根文件系统
# 起始于 104448 扇区,结束于最后一个可用扇区(-1)
sudo parted imx6ull.img --script -- mkpart primary ext4 104448s -1

# 将镜像文件关联到第一个可用的 loop 设备,并显示设备名(如 /dev/loop0)
sudo losetup -f --show imx6ull.img

# 为 loop 设备创建分区映射(生成 /dev/mapper/loop0p1 和 loop0p2)
sudo kpartx -va /dev/loop0

# 格式化第一个分区为 vfat(FAT32)
sudo mkfs.vfat /dev/mapper/loop0p1

# 格式化第二个分区为 ext4
sudo mkfs.ext4 /dev/mapper/loop0p2

# 挂载两个分区到先前创建的目录
sudo mount /dev/mapper/loop0p1 ./sd/boot/
sudo mount /dev/mapper/loop0p2 ./sd/root/

# 将 U-Boot 镜像(u-boot-dtb.imx)写入镜像的偏移 2 个扇区处(跳过前 1KB)
# 因为 i.MX6ULL 从偏移 1KB(即第 2 个 block)处加载 U-Boot
#U-Boot 不是放在SD卡分区里面的文件,而是放在SD卡最前面的固定裸地址区域,i.MX6ULL芯片上电后会直接#去这个位置找它。
sudo dd if=/opt/out/u-boot-dtb.imx of=/dev/loop0 bs=512 seek=2 conv=fsync

# 复制启动相关文件(内核、设备树等)到 boot 分区
sudo cp ./all/boot/* ./sd/boot/

# 复制根文件系统内容到 rootfs 分区(保留属性,-a 表示归档模式)
sudo cp -a ./all/rootfs/* ./sd/root/

# 卸载两个分区
sudo umount ./sd/boot/ ./sd/root/

# 移除分区映射
sudo kpartx -d /dev/loop0

# 释放 loop 设备
sudo losetup -d /dev/loop0

# 提示打包开始(压缩镜像,使用 xz 格式,减少体积)
echo "正在打包,需要一定时间,请勿中断"
xz -zf ./imx6ull.img

# 清理临时目录(这里删除的是 ./gxa,但脚本中并未创建该目录,可能是用户失误或额外清理)
rm -rf ./gxa





---------------------------------------备用

# 创建两个目录用于挂载镜像的分区
mkdir -p sd/boot sd/root

# 创建 200MB 镜像
dd if=/dev/zero of=./imx6ull.img bs=512 count=409600

# 创建 MBR 分区表
sudo parted imx6ull.img --script -- mklabel msdos

# 创建 boot 分区
sudo parted imx6ull.img --script -- mkpart primary fat32 2048s 104447s

# 创建 rootfs 分区
sudo parted imx6ull.img --script -- mkpart primary ext4 104448s -1


# 绑定 loop
LOOP=$(sudo losetup -f --show imx6ull.img)

echo "使用设备: $LOOP"


# 创建分区映射
sudo kpartx -av $LOOP


# 获取分区设备名字
PART1="/dev/mapper/$(basename $LOOP)p1"
PART2="/dev/mapper/$(basename $LOOP)p2"

echo "boot分区: $PART1"
echo "root分区: $PART2"


# 格式化
sudo mkfs.vfat $PART1
sudo mkfs.ext4 $PART2


# 挂载
sudo mount $PART1 ./sd/boot/
sudo mount $PART2 ./sd/root/


# 写入 U-Boot
sudo dd if=/home/xingxinliao/ebf/out/u-boot-dtb.imx of=$LOOP bs=512 seek=2 conv=fsync


# 拷贝 boot 文件
sudo cp /home/xingxinliao/ebf/out/* ./sd/boot/


# 拷贝 rootfs
sudo cp -a /home/xingxinliao/ebf/rootsfs/* ./sd/root/


# 卸载
sudo umount ./sd/boot/
sudo umount ./sd/root/


# 删除分区映射
sudo kpartx -d $LOOP


# 释放 loop
sudo losetup -d $LOOP


echo "正在打包,需要一定时间,请勿中断"

xz -zf ./imx6ull.img


rm -rf ./gxa

3.7 shell脚本语法解析参考

dd就是复制

  • 可以复制磁盘
  • 可以复制分区
  • 可以按指定大小复制数据块
  • 可以制作镜像文件

dd if=/dev/zero of=./imx6ull.img bs=512 count=409600 的作用就是:创建一个200MB大小的空白SD卡镜像文件 imx6ull.img

dd if=输入文件 of=输出文件

if=/dev/zero of输入0

输出到of=./imx6ull.img

相关推荐
一直走下去-明2 小时前
centos7无法安装tcpreplay
linux·运维
吹什么轩2 小时前
linux网络:TCP套接字基础
linux·网络·tcp/ip
anxiao_m3 小时前
园区数字孪生怎么搭建?主流方案与服务商深度对比
大数据·运维·人工智能
时空无限3 小时前
ubuntu dpkg -l 输出第一列解释
linux·运维·ubuntu
Splashtop高性能远程控制软件3 小时前
AI 落地的连接基建评估:消费品行业远程连接方案的四个技术维度(性能 / 精度 / 安全 / 统一管理)
运维·人工智能·安全·远程工作·splashtop
ITyunwei09874 小时前
技术管理者视角:AI 重构 ITSM 的四个工程化落点
运维·人工智能
雪碧聊技术5 小时前
使用Docker,将fastApi项目部署到linux服务器
服务器·docker·fastapi·docker部署fastapi
叮咚侠5 小时前
Ubuntu 24.04 安装zabbix-agent 5.0 服务
linux·ubuntu·zabbix·zabbix-agent