当前位置: 首页 > news >正文

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

目录

1. 项目目标

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

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

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

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

3.3 修改Uboot源码并编译

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

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

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


1. 项目目标

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

最终产物:imx6ull.img

工厂拿到这个文件后:

SD卡 ↓ 烧写 imx6ull.img ↓ 上电 ↓ 自动启动Linux系统

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

一个嵌入式 Linux 开机过程:

上电 | 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)~50MBFAT32Uboot二进制(u-boot.imx)、内核镜像(zImage)、设备树(.dtb)、可选的initrd(ramdisk.gz
第2分区(EXT4)~150MBEXT4根文件系统(即完整的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等命令)。

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

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),添加或修改以下宏定义:

"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分区),以便拷贝新内核或设备树。

示例脚本内容:

#!/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就是完整的量产镜像。

    sudo dd if=/dev/sdb of=imx6ull.img bs=4M status=progress

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

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

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

    sudo dd if=imx6ull.img of=/dev/sdX bs=4M status=progress oflag=sync

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

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

3.6 shell脚本参考

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

# 创建两个目录用于挂载镜像的分区 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

http://www.cnnetsun.cn/news/4070936.html

相关文章:

  • 免费离线语音转文字工具 TMSpeech 完整指南:把电脑声音实时变成文字
  • 移动端设备标识码全解析:从IMEI到UUID的合规实践指南
  • DTU上传数据一定要公网IP吗?
  • BBDown 快速上手指南:命令行一键下载 B 站视频,最高支持 8K 与杜比视界
  • UVa 701 The Archeologist‘s Dilemma
  • CK2dll双字节补丁快速上手全攻略:从安装到调教十字军之王II中文显示
  • 不用硬憋论文!Paperxie智能写作|专治写作空白、无思路、内容水难题
  • A06 | FMEDA 实战与故障模式:从元器件失效率到系统级 PMHF 的完整计算链
  • TIA Portal 21安装教程
  • #7、Spring AI 使用 MCP 客户端(调用高德 MCP)
  • 30 分钟把 100+ 安全工具拧成一个智能体:CyberStrikeAI 实战手记
  • AI开发回归科学:从大模型幻觉到Agent落地的工程实践指南
  • Equalizer APO 系统级音频均衡完整指南:5 个阶段从装好到玩出花样
  • 2026保研培训机构哪家靠谱?五维实力评估与择校全攻略
  • Oracle日期与字符串转换:核心函数、隐式转换陷阱与性能优化
  • Day15 unitree_G1人形机器人“身外化身”通信丢帧排查
  • Obsidian 主页模板零基础实测:把杂乱笔记库一键变成清爽仪表盘
  • 中小企业出入库系统推荐:2026 年 TOP5 易上手软件,金蝶 AI 星辰实现效率翻倍
  • 南京甄选专业GEO服务商的关键维度与行业实践参考
  • 电视盒子刷Armbian变身Linux家庭服务器:从U盘启动到应用部署的4阶闯关指南
  • 老电视看直播不再卡顿!10分钟用MyTV-Android解锁流畅电视直播的保姆级教程
  • 如何用Wand-Enhancer免费解锁WeMod高级功能:安装、远程控制与自定义脚本完整指南
  • Sqlite-graphrag 7.3MB 单文件 AI 图谱知识库引擎,私有AI知识库
  • Android InputDispatcher 跨 Display 触摸事件丢失分析
  • 一文搞懂WeTextProcessing:让语音与文本处理项目告别数字乱码的归一化利器
  • Bitwarden:开源免费的跨平台密码管理器,端到端加密
  • 热门八股-JUC
  • 特斯拉Model 3如何以鲶鱼效应重塑中国新能源汽车产业格局
  • 江西五十铃新款D-MAX谍照解析:中期改款设计、动力与市场前瞻
  • 网盘直链下载怎么玩才不折腾?我的两年亲测与避坑记录