Linux 工控项目打包、烧录、批量量产部署完整流程
Linux 工控项目打包、烧录、批量量产部署完整流程从开发板到出厂出货中间隔着一条完整的量产流水线——这流程搞不定代码写得再好也只是实验室玩具。一、工控项目交付物构成一个完整的Linux工控固件包通常包含以下组件组件文件说明Bootloaderuboot.img / idbloader.img引导加载器负责硬件初始化和加载内核内核kernel.img / zImage / ImageLinux内核镜像设备树dtb文件硬件描述告诉内核板子上有什么外设根文件系统rootfs.img包含所有用户空间程序和库应用包app.tar.gz / app.ipk你的工控应用程序配置文件config/默认配置、网络参数等firmware_v1.0.0/ ├── idbloader.img # Bootloader阶段1 ├── uboot.img # Bootloader阶段2 ├── kernel.img # Linux内核 ├── rk3568-board.dtb # 设备树 ├── rootfs.img # 根文件系统ext4/squashfs ├── app/ │ ├── myapp # 主程序 │ ├── lib/ # 依赖库 │ └── config/ # 配置文件 └── version.txt # 版本信息二、固件打包工具2.1 mkimage —— U-Boot镜像封装mkimage是U-Boot自带的镜像打包工具给内核镜像加上U-Boot识别的头信息# 将zImage打包成uImagemkimage-Aarm64-Olinux-Tkernel-Cnone\-a0x80080000-e0x80080000\-nLinux Kernel\-darch/arm64/boot/Image uImage# 参数说明# -A 架构arm64/x86/mips# -O 操作系统linux# -T 镜像类型kernel/ramdisk/script# -C 压缩方式none/gzip/bzip2# -a 加载地址# -e 入口地址# -n 镜像名称# -d 输入文件2.2 genext2fs —— 制作ext2文件系统镜像# 根据目录生成ext2文件系统镜像genext2fs-b32768-drootfs_dir rootfs.img# -b 块数每块1KB32768块32MB# -d 源目录2.3 mksquashfs —— 制作只读压缩文件系统squashfs是工控设备根文件系统的首选只读压缩掉电不损坏# 将rootfs目录打包成squashfs镜像mksquashfs rootfs_dir rootfs.squashfs-compxz -no-xattrs# -comp 压缩算法xz/gzip/lzo/zstd# xz压缩率最高lzo解压最快2.4 dd —— 磁盘镜像工具# 将SD卡内容完整导出为镜像ddif/dev/sdbofsdcard.imgbs4Mstatusprogress# 将镜像写入SD卡ddifsdcard.imgof/dev/sdbbs4Mstatusprogressconvfsync三、SD卡烧录3.1 分区方案典型的SD卡分区布局| 分区 | 偏移 | 大小 | 内容 | 文件系统 | |------|------|------|------|----------| | idbloader | 0x40 | 32KB | Bootloader | 无 | | uboot | 0x4000 | 4MB | U-Boot | 无 | | boot | 0x8000 | 64MB | 内核DTB | FAT32 | | rootfs | 0x88000 | 剩余 | 根文件系统 | ext4 |3.2 烧录脚本#!/bin/bash# /opt/scripts/sdcard_burn.sh# SD卡烧录脚本SDCARD_DEV$1# /dev/sdbFIRMWARE_DIR$2# firmware_v1.0.0/if[-z$SDCARD_DEV]||[-z$FIRMWARE_DIR];thenechoUsage:$0sdcard_dev firmware_direcho e.g:$0/dev/sdb ./firmware_v1.0.0/exit1fiechoecho SD Card Burning Toolecho Device:$SDCARD_DEVecho Firmware:$FIRMWARE_DIRecho# 安全确认echoWARNING: All data on$SDCARD_DEVwill be erased!echoType YES to continue:read-rCONFIRMif[$CONFIRM!YES];thenechoAborted.exit0fi# 1. 卸载所有已挂载的分区umount${SDCARD_DEV}?*2/dev/null# 2. 写入Bootloaderecho[1/5] Writing idbloader...ddif${FIRMWARE_DIR}/idbloader.imgof$SDCARD_DEVbs32kseek1convfsyncecho[2/5] Writing uboot...ddif${FIRMWARE_DIR}/uboot.imgof$SDCARD_DEVbs64kseek128convfsync# 3. 创建分区echo[3/5] Creating partitions...# 清空分区表ddif/dev/zeroof$SDCARD_DEVbs512count2048convfsync# 用fdisk或sfdisk创建分区sfdisk$SDCARD_DEVEOF label: dos${SDCARD_DEV}1 : start8192, size131072, typec, bootable${SDCARD_DEV}2 : start139264, type83 EOF# 刷新分区表partprobe$SDCARD_DEV2/dev/nullsleep2# 4. 格式化分区echo[4/5] Formatting partitions...mkfs.vfat-F32-nBOOT${SDCARD_DEV}1mkfs.ext4-F-Lrootfs${SDCARD_DEV}2# 5. 写入内核和根文件系统echo[5/5] Writing kernel and rootfs...mkdir-p/tmp/sd_boot /tmp/sd_rootmount${SDCARD_DEV}1/tmp/sd_bootmount${SDCARD_DEV}2/tmp/sd_root# 拷贝内核和DTB到boot分区cp${FIRMWARE_DIR}/kernel.img/tmp/sd_boot/cp${FIRMWARE_DIR}/*.dtb /tmp/sd_boot/# 解压rootfs到root分区if[-f${FIRMWARE_DIR}/rootfs.tar.gz];thentar-xzf${FIRMWARE_DIR}/rootfs.tar.gz-C/tmp/sd_root/elif[-f${FIRMWARE_DIR}/rootfs.img];thenddif${FIRMWARE_DIR}/rootfs.imgof${SDCARD_DEV}2bs4Mconvfsyncfisyncumount/tmp/sd_boot /tmp/sd_rootechoecho Burning Complete!echo四、eMMC/NAND烧录4.1 fastboot通用Android协议# 进入fastboot模式U-Boot命令行中fastboot0# 主机端烧录fastboot flash loader idbloader.img fastboot flash uboot uboot.img fastboot flash boot_a kernel.img fastboot flash dtbo_a rk3568-board.dtb fastboot flash system_a rootfs.img# 切换启动槽位并重启fastboot set_active a fastbootreboot4.2 rkflashtool瑞芯微专用# 查看连接的设备rkflashtool p# 读取分区表rkflashtool r 0x0 0x2000 param.img# 烧录各分区rkdeveloptool db RK3568_loader.bin rkdeveloptool wl 0x40 idbloader.img rkdeveloptool wl 0x4000 uboot.img rkdeveloptool wl 0x8000 boot.img rkdeveloptool wl 0x88000 rootfs.img# 重启设备rkdeveloptool rd4.3 nandwriteNAND Flash直接写入# 先擦除flash_erase /dev/mtd200# 写入镜像nandwrite-p/dev/mtd2 rootfs.ubi# UBI方式NAND推荐ubiformat /dev/mtd2-s2048-frootfs.ubi-yubiattach /dev/ubi_ctrl-m2mount-tubifs /dev/ubi0_0 /mnt/rootfs五、量产烧录自动化批量烧录场景下需要自动检测设备插入并触发烧录#!/bin/bash# /opt/scripts/mass_burn.sh# 批量SD卡烧录脚本配合USB Hub多卡同时烧FIRMWARE_DIR/opt/firmware/currentLOG_FILE/var/log/mass_burn.logBURN_SCRIPT/opt/scripts/sdcard_burn.shecho$(date)[INFO] Mass burn service started$LOG_FILE# udev规则SD卡插入时自动触发# /etc/udev/rules.d/99-sdburn.rules:# ACTIONadd, KERNELsd[b-z], SUBSYSTEMblock, \# RUN/opt/scripts/mass_burn.sh /dev/%kDEVICE$1if[-z$DEVICE];thenecho$(date)[ERROR] No device specified$LOG_FILEexit1fi# 等待设备稳定sleep3# 检查设备是否存在if[!-b$DEVICE];thenecho$(date)[ERROR] Device$DEVICEnot found$LOG_FILEexit1fiecho$(date)[INFO] Start burning$DEVICE$LOG_FILE# 调用烧录脚本非交互模式YESYES$BURN_SCRIPT$DEVICE$FIRMWARE_DIR$LOG_FILE21if[$?-eq0];thenecho$(date)[OK]$DEVICEburned successfully$LOG_FILE# 闪烁指示灯或播放提示音echo1/sys/class/leds/green/brightnesssleep1echo0/sys/class/leds/green/brightnesselseecho$(date)[ERROR]$DEVICEburn failed!$LOG_FILEecho1/sys/class/leds/red/brightnessfi六、量产部署完整流程固件版本管理 │ ▼ ┌─────────────┐ │ 1. 编译打包 │ ← CI/CD自动构建产出固件包 └──────┬──────┘ ▼ ┌─────────────┐ │ 2. 烧录固件 │ ← SD卡/eMMC烧录 └──────┬──────┘ ▼ ┌─────────────┐ │ 3. 首次启动 │ ← 系统初始化、分区扩展 └──────┬──────┘ ▼ ┌─────────────┐ │ 4. 功能验证 │ ← 自动化测试脚本 └──────┬──────┘ ▼ ┌─────────────┐ │ 5. 烧录MAC │ ← 写入唯一MAC/序列号 └──────┬──────┘ ▼ ┌─────────────┐ │ 6. 出库登记 │ ← 记录序列号、版本、日期 └─────────────┘6.1 固件版本管理# /etc/version 文件cat/etc/version# FW_VERSION1.0.3# FW_DATE2026-07-13# FW_GIT_COMMITa1b2c3d# HW_VERSIONV2.1与git tag关联# 发布固件时打taggittag-av1.0.3-mRelease v1.0.3: fix watchdog timeout issuegitpush origin v1.0.3# 构建脚本读取tag写入版本文件GIT_TAG$(gitdescribe--tags--always)BUILD_DATE$(date%Y-%m-%d)GIT_COMMIT$(gitrev-parse--shortHEAD)catrootfs/etc/versionEOF FW_VERSION${GIT_TAG}FW_DATE${BUILD_DATE}FW_GIT_COMMIT${GIT_COMMIT}HW_VERSIONV2.1 EOF6.2 首次启动配置脚本#!/bin/bash# /opt/scripts/firstboot.sh# 首次启动初始化通过systemd firstboot服务触发MARKER/etc/.firstboot_doneif[-f$MARKER];thenexit0# 已执行过fiecho First Boot Initialization # 1. 扩展rootfs分区到整个磁盘ROOT_DEV$(findmnt /-oSOURCE-n)PART_NUM$(echo$ROOT_DEV|grep-o[0-9]*$)# resize2fs扩展ext4文件系统resize2fs$ROOT_DEV# 2. 生成SSH密钥ssh-keygen-A# 3. 设置时区ln-sf/usr/share/zoneinfo/Asia/Shanghai /etc/localtime# 4. 生成唯一设备IDechoDEV_ID$(cat/proc/sys/kernel/random/uuid)/etc/device_id# 5. 启用核心服务systemctlenablemyapp.service systemctlenablesshd.servicetouch$MARKERecho First Boot Done reboot七、OTA远程升级方案7.1 A/B分区切换方案| 分区 | 内容 | 状态 | |------|------|------| | boot_a | 内核槽位A | 当前活跃 | | boot_b | 内核槽位B | 备用 | | system_a | 根文件系统A | 当前活跃 | | system_b | 根文件系统B | 备用 |升级流程#!/bin/bash# /opt/scripts/ota_update.shNEW_FW$1# 新固件包路径# 1. 确定当前活跃槽位ACTIVE_SLOT$(bootctl status|grepCurrent Slot|awk{print $3})if[$ACTIVE_SLOTa];thenTARGET_SLOTbelseTARGET_SLOTafiechoActive:$ACTIVE_SLOT, Target:$TARGET_SLOT# 2. 写入新固件到非活跃槽位fastboot flash boot_${TARGET_SLOT}${NEW_FW}/kernel.img fastboot flash system_${TARGET_SLOT}${NEW_FW}/rootfs.img# 3. 设置下次启动使用新槽位bootctl set_active$TARGET_SLOT# 4. 重启尝试新固件reboot# 5. 如果新固件正常应用确认在应用中调用# bootctl mark_successful# 如果启动失败bootloader自动回滚到旧槽位7.2 增量升级如果只是应用层更新不需要刷整包用差分包更高效# 生成差分包bsdiff old_app.tar.gz new_app.tar.gz app_patch.diff# 设备端应用差分bspatch old_app.tar.gz new_app.tar.gz app_patch.diff八、烧录常见问题排查问题可能原因解决方案dd写入失败SD卡写保护/损坏检查写保护开关换卡设备不启动Bootloader未烧录或地址错误检查偏移地址确认idbloader写入位置内核启动后挂死DTB不匹配确认DTB与硬件版本一致rootfs挂载失败文件系统损坏重新格式化分区检查镜像完整性烧录后分区大小不对未做首次扩展执行resize2fs扩展分区fastboot无法连接USB驱动/线缆换数据线检查udev规则8.1 验证镜像完整性# 生成校验和sha256sum uboot.img kernel.img rootfs.imgchecksums.sha256# 设备端校验sha256sum-cchecksums.sha256# uboot.img: OK# kernel.img: OK# rootfs.img: OK九、量产部署Checklist序号检查项状态1固件版本号已更新并打git tag☐2所有镜像SHA256校验通过☐3SD卡/eMMC烧录成功无报错☐4设备首次启动正常串口有输出☐5首次启动脚本执行完成☐6rootfs分区已扩展到完整容量☐7网口可通信IP获取正常☐8串口数据收发正常☐9看门狗功能验证通过☐10应用程序自动启动且运行正常☐11MAC地址/序列号已烧录☐12重启3次无异常☐13出库信息登记序列号/版本/日期☐量产部署的核心是标准化和可重复。每一步都有明确的检查项每一台设备都走同样的流程不依赖人的经验这样才能保证100台设备100台稳定。