RDK X5 的镜像打包阶段是整个固件构建流水线的最终收口环节,它将前置阶段产出的内核、Deb 包与 Sample RootFS 合并为一可烧录的磁盘镜像。本章聚焦于 pack_image.sh 的编排逻辑、hobot_customize_rootfs.sh 的系统定制策略,以及分区布局与 Deb 包依赖管理的实现细节,帮助开发者掌握从“零散构件”到“完整系统镜像”的完整转化过程。
Sources: pack_image.sh, hobot_customize_rootfs.sh
镜像打包总览
镜像打包遵循**“下载 → 解压 → 定制 → 合入 Deb → 分区 → 同步”**的六阶段流水线。在 build.sh 的统一编排下,pack 子命令负责调用 pack_image.sh 完成最终镜像的生成,而 pack_image.sh 本身又依赖 download_samplefs.sh 与 download_deb_pkgs.sh 获取外部资源。整个流程以配置文件(build_params/*.conf)为唯一输入源,所有路径、包名、版本号均通过 source "$CONFIG_FILE" 注入。
flowchart TD
A[build_params/*.conf] --> B[pack_image.sh]
B --> C{LOCAL_BUILD?}
C -->|false| D[download_samplefs.sh]
C -->|false| E[download_deb_pkgs.sh]
C -->|true| F[跳过下载]
D --> G[samplefs tarball]
E --> H[hobot debs]
G --> I[解压至 ROOTFS_BUILD_DIR]
H --> J[复制至 /app/hobot_debs]
I --> K[hobot_customize_rootfs.sh]
K --> L[安装 Deb 包]
L --> M[计算分区大小]
M --> N[parted 创建 MBR 分区]
N --> O[mkfs.ext4 + mkdosfs]
O --> P[rsync 同步]
P --> Q[deploy/*.img]
该架构的关键设计在于**“双层 RootFS”**工作空间:ROOTFS_ORIG_DIR 存放原始 samplefs 压缩包,ROOTFS_BUILD_DIR 作为可写构建目录,最终通过 rsync 同步到由 losetup 挂载的 loop 设备分区中。这种分层设计避免了污染原始样本文件系统,同时支持在构建目录中执行 chroot 操作。
Sources: pack_image.sh, build.sh
打包脚本执行流程
pack_image.sh 是镜像生成的核心脚本,必须以 root 身份执行,默认加载 ubuntu-22.04_desktop_rdk-x5_release.conf,并支持 -c 指定配置与 -l 启用本地构建(跳过下载)。脚本首先初始化 IMAGE_DEPLOY_DIR 作为最终产出目录,随后进入 make_ubuntu_image 函数执行实质性工作。
函数内部首先通过 tar 将 samplefs 解压至 ROOTFS_BUILD_DIR,并补全系统必需的挂载点目录(/proc、/sys、/dev、/tmp、/run 等),同时将 RDK_IMAGE_VERSION 写入 /etc/version。紧接着调用 hobot_customize_rootfs.sh 对系统进行深度定制,随后将三类 Deb 包源——RDK_DEB_PKG_DIR(官方包)、RDK_THIRD_DEB_PKG_DIR(第三方包)以及 deploy/deb_pkgs(用户自定义包)——合并到 /app/hobot_debs。在执行安装前,脚本会通过版本号比较自动去重同名包,仅保留最新版本,这一机制在并行构建或多版本迭代时尤为重要。
Sources: pack_image.sh
RT 内核切换逻辑
针对 X5 平台,脚本在完成 Deb 安装后会执行一项关键的平台适配操作:修改 U-Boot 启动脚本以加载 RT 内核镜像。具体实现为读取 source/hobot-boot/debian/boot/boot.cmd,将其中的 imagefile="Image" 替换为 imagefile="Image-rt",再使用 mkimage 重新生成 boot.scr 并放置于构建目录的 /boot 路径下。这意味着最终烧录的镜像默认以实时内核启动,开发者若需回退至标准内核,需在启动前手动修改 boot.cmd 并重新打包 boot.scr。
Sources: pack_image.sh
根文件系统定制
hobot_customize_rootfs.sh 以函数形式提供,接收一个参数 DST_ROOTFS_DIR,所有修改均通过直接编辑目标路径下的配置文件或在 chroot 环境中执行命令完成。其定制内容可分为系统服务、网络配置、用户管理和安全策略四大维度。
系统服务维度,脚本禁用了 NetworkManager-wait-online.service 与 ondemand.service,并将 networking.service 的超时阈值从 5 分钟缩减至 2 秒,以加速嵌入式设备的启动流程。同时通过设置 fake-hwclock.data 为当前时间,避免无 RTC 硬件时系统时间回到 Epoch。脚本还关闭了无人值守升级(Unattended-Upgrade "0")和版本升级提示(Prompt=never),确保系统状态在部署后保持稳定可控。
网络配置维度,脚本强制启用 NetworkManager 的托管模式(managed=true),并设置 DNS 策略为 default 且 rc-manager=file,使 /etc/resolv.conf 作为常规文件而非软链接存在,避免某些容器或 chroot 场景下的解析异常。若环境变量 NM_IGNORE_DEVICES 已定义,还会在 /etc/NetworkManager/conf.d/ 下生成 10-ignore-interfaces.conf,排除特定网卡接口。
用户管理维度,脚本预创建了一系列硬件访问组,包括 audio、gpio、i2c、video、misc、vps、ipu、jpu、graphics、render、vpu、kmem、dialout、disk 等,并将这些组写入 /etc/adduser.conf 的 EXTRA_GROUPS,确保新创建的用户自动获得硬件外设访问权限。随后创建普通用户 sunrise(密码同为 sunrise),并将其加入 sudo 及各硬件组;同时设置 root 密码为 root。最后将 /etc/skel 下的默认配置文件复制到 /root 与 /home/sunrise,保证 root 与普通用户拥有统一的 Shell 环境。
SSH 安全维度,脚本修改 sshd_config 以允许 root 直接登录(PermitRootLogin yes)并启用公钥认证(PubkeyAuthentication yes),这在产线首次烧录和自动化部署阶段极为实用,但生产环境建议通过外部配置覆盖该设置。
Sources: hobot_customize_rootfs.sh
分区布局与镜像生成
pack_image.sh 采用经典的双分区 MBR 方案,分区表通过 parted 写入镜像文件,文件系统则分别格式化为 FAT32(配置分区)与 ext4(根分区)。
| 分区 | 文件系统 | 起始位置 | 大小计算方式 | 用途 |
|---|---|---|---|---|
| p1 | FAT32 (mkdosfs) | 4 MiB | 固定 256 MiB,按 4 MiB 对齐 | 存放启动配置、设备树 overlay 等 |
| p2 | ext4 (mkfs.ext4) | 260 MiB | ROOT_SIZE * 1.2 + 200 MiB,按 4 MiB 对齐 |
Ubuntu 根文件系统 |
其中 ROOT_SIZE 通过 du --apparent-size -s 计算构建目录的占用字节数,并排除 /var/cache/apt/archives 与 /boot/config;ROOT_MARGIN 以 ROOT_SIZE * 0.2 + 200 * 1024 * 1024 的公式通过 bc 计算,既预留了文件系统元数据开销,也为终端用户保留了一定的可用空间。创建镜像文件后,脚本通过 losetup --find --partscan 绑定 loop 设备,对 p1 与 p2 分别格式化,随后挂载 p2 到临时目录 ROOTFS_DIR,并在其下创建 /boot/config 子目录后挂载 p1。最后使用 rsync -aHAXx 将 ROOTFS_BUILD_DIR 的内容同步至 ext4 分区,同时将 config/ 目录的内容同步至 vfat 配置分区。
Sources: pack_image.sh
清理与卸载机制
脚本封装了 unmount_image 函数用于安全卸载:首先执行 sync 并休眠 1 秒,随后通过 losetup --list 查询与目标镜像关联的 loop 设备,依次卸载其各分区的挂载点,最后调用 losetup -d 释放设备。该函数在镜像生成前后均被调用,确保即使前次构建异常退出残留 loop 设备,也不会影响本次构建。
Sources: pack_image.sh
Deb 包管理与安装策略
镜像打包阶段的 Deb 包来源具有三重渠道,优先级由文件系统复制顺序自然形成:本地编译产出(deb_packages/)、第三方闭源包(third_packages/)以及用户自定义包(deploy/deb_pkgs/)。所有包最终汇聚于构建目录的 /app/hobot_debs,安装完成后该目录会被整体删除,避免镜像膨胀。
版本去重算法
在调用安装前,脚本执行了一项精密的版本去重逻辑:对 /app/hobot_debs 下的所有 .deb 文件按字典序排序,通过 awk -F"_" 提取包名与版本号,使用 dpkg --compare-versions 进行语义化版本比较,仅保留每个包名对应的最新版本。该机制有效解决了多次构建或合并多源包时可能出现的同名不同版本冲突。
Sources: pack_image.sh
Chroot 安装与依赖处理
install_packages 函数遍历去重后的 Deb 列表,对每个包调用 install_deb_chroot。该函数首先针对 xserver 包做特殊守卫:若根文件系统中未检测到 xserver 环境,则跳过安装,避免在 Server 变体镜像中引入不必要的图形依赖。对于常规包,函数通过 dpkg-deb -f 读取 Depends 字段并去除版本约束括号,随后以 dpkg --ignore-depends 强制安装,此举是为了防止在离线 chroot 环境中因无法满足动态依赖而导致安装中断。所有包安装完毕后,脚本在 chroot 内执行 apt clean 清理本地缓存。
Sources: pack_image.sh
远程包下载与依赖解析
当未启用 -l 本地构建时,download_deb_pkgs.sh 会从 RDK_ARCHIVE_URL 指定的 APT 仓库中拉取 Deb 包。其工作流程为:首先下载 Packages 索引文件,然后遍历 RDK_DEB_PKG_LIST 数组,利用 awk 在 Packages 文件中定位每个包的最新版本、文件名、MD5 校验和及依赖列表。依赖解析采用递归策略,但仅保留名称匹配 ^xserver 的依赖项进行递归下载,其余 hobot 生态的依赖假定由本地编译提供,避免过度拉取外部包。
下载过程具备完整性校验(MD5 比对)、断点续传前的旧版本清理,以及网络超时与重试机制(--connect-timeout 20 --retry 3)。
Sources: download_deb_pkgs.sh
构建系统集成
在 build.sh 的顶层编排中,pack 子命令不仅调用 pack_image.sh,还负责产物的后处理。具体而言,do_pack 函数以 -l 标志调用 pack_image.sh(因为在 build.sh 的完整流水线中,samplefs 与 debs 已由前序阶段准备就绪),待镜像生成后计算 SHA256 校验和并创建 ZIP 压缩包。这意味着开发者执行 sudo ./build.sh pack 时,最终将在 deploy/ 目录下同时获得 .img、.img.sha256 与 .zip 三种产物。
Sources: build.sh
本地构建与远程下载模式对比
| 模式 | 触发方式 | SampleFS 来源 | Deb 来源 | 适用场景 |
|---|---|---|---|---|
| 全量远程 | pack_image.sh 无 -l |
download_samplefs.sh 从 archive 下载 |
download_deb_pkgs.sh 从 APT 仓库下载 |
首次构建或 CI 环境 |
| 本地增量 | pack_image.sh -l |
使用 rootfs/ 下已有的 tarball |
使用 deb_packages/、third_packages/、deploy/deb_pkgs/ 下的本地包 |
重复打包或自定义包验证 |
| 构建流水线 | build.sh pack |
依赖前序 rootfs 阶段产出 |
依赖前序 debs 阶段产出 |
完整的本地端到端构建 |
Sources: pack_image.sh, build.sh
配置参数与镜像变体
镜像名称、包列表与下载地址完全由 build_params/ 下的配置脚本定义。对比 ubuntu-22.04_desktop_rdk-x5_release.conf 与 ubuntu-22.04_server_rdk-x5_release.conf 可见两个关键差异:一是 RDK_IMAGE_TYPE 与 RDK_IMAGE_NAME 决定了最终镜像的文件名与 samplefs 类型;二是 RDK_DEB_PKG_LIST 中 Desktop 变体额外包含 xserver-xorg-core,而 Server 变体将其剔除,从而保证 Server 镜像不包含 X11 相关组件。
| 配置项 | Desktop 变体 | Server 变体 | 说明 |
|---|---|---|---|
RDK_IMAGE_TYPE |
desktop |
server |
决定 samplefs 类型 |
RDK_ROOTFS_DIR |
rootfs |
rootfs_server |
解压/存放路径 |
RDK_DEB_PKG_LIST |
含 xserver-xorg-core |
不含 xserver-xorg-core |
图形栈差异 |
RDK_IMAGE_NAME |
...desktop...img |
...server...img |
最终镜像文件名 |
Sources: build_params/ubuntu-22.04_desktop_rdk-x5_release.conf, build_params/ubuntu-22.04_server_rdk-x5_release.conf
自定义扩展指南
开发者在实际项目中常需向镜像注入自定义 Deb 包或修改系统默认配置。基于现有架构,推荐以下两种扩展方式。
注入自定义 Deb 包:将自行编译或外部获取的 .deb 文件放置于 x5-rdk-gen/deploy/deb_pkgs/ 目录下,执行 sudo ./build.sh pack 即可自动合入镜像。该目录在 pack_image.sh 中的复制逻辑位于官方包与第三方包之后,因此若存在同名包且版本更高,去重算法会保留你的版本。对于需要跨镜像复用的自定义包,亦可创建新的目录并在配置文件中定义 RDK_THIRD_DEB_PKG_DIR 指向该路径。
修改系统默认配置:直接编辑 hobot_customize_rootfs.sh 是最直接的方式,例如修改默认用户名、密码、主机名或 SSH 策略。若仅需覆盖启动配置或设备树,可将修改后的文件放入 config/ 目录,该目录的内容会在镜像生成最后阶段通过 rsync 同步至 /boot/config(vfat 分区),适合存放不依赖 ext4 文件系统的启动参数。
Sources: pack_image.sh, pack_image.sh
关联阅读与后续步骤
掌握镜像打包与系统定制后,建议继续深入了解以下关联主题:
- 若需理解内核与引导加载程序如何前置构建,请参阅 双内核编译与实时内核 与 全量构建与子命令解析。
- 若需自定义 Deb 包编译流程与依赖管理,请参阅 Deb 包本地编译与依赖管理。
- 若需调整构建参数或创建新的镜像变体,请参阅 构建配置与参数管理。
- 若需将 RDK X5 镜像集成到更大的机器人固件发布体系中,请参阅 机器人系统固件集成策略。