🤖 roboto_origin_03 Wiki
首页 / 固件 / 根文件系统与镜像打包

本文档深入解析 OrangePi 构建系统中根文件系统(RootFS)的创建、定制与镜像打包的完整技术链路。内容覆盖从 debootstrap 基础系统构建、多层配置叠加、本地 Deb 包集成,到分区布局、文件系统选择、镜像压缩与签名的全生命周期。理解这些机制是进行镜像裁剪、自定义包集成和构建故障排查的必要前提。

根文件系统构建总览

整个根文件系统与镜像打包流程由 debootstrap_ng() 函数在 main.sh 的构建收尾阶段统一调度。流程可分为三大阶段:根文件系统制备create_rootfs_cache 与多层定制)、分区与镜像文件制备prepare_partitions)、镜像组装与输出create_image)。系统通过 SDCARD 变量指向的根文件系统工作目录完成所有 chroot 操作,最终通过 rsync 将内容同步到由 loop 设备挂载的镜像分区中。构建入口设置了一个 trap unmount_on_exit INT TERM EXIT 陷阱,确保在手动中断或错误退出时自动清理挂载点和 loop 设备,避免宿主机残留污染状态。

flowchart TD
    A[main.sh 调用 debootstrap_ng] --> B[create_rootfs_cache<br/>缓存命中解压 / 未命中 debootstrap]
    B --> C[pre_install_distribution_specific<br/>扩展 Hook]
    C --> D[install_distribution_specific<br/>发行版特定配置]
    D --> E[install_common<br/>通用系统定制]
    E --> F[chroot_installpackages_local<br/>本地编译包集成]
    F --> G[customize_image<br/>用户自定义脚本]
    G --> H[post_debootstrap_tweaks<br/>清理服务拦截]
    H --> I{ROOTFS_TYPE?}
    I -->|fel| J[FEL 启动]
    I -->|cix| K[CIX 专用镜像]
    I -->|others| L[prepare_partitions<br/>分区/格式化/挂载]
    L --> M[create_image<br/>rsync / initramfs / u-boot / 压缩]

Sources: debootstrap.sh main.sh

根文件系统缓存机制

构建系统采用基于内容寻址的缓存策略以避免重复执行完整的 debootstrap 流程。缓存文件名由 get_package_list_hash() 生成 MD5 校验值,格式为 ${RELEASE}-${cache_type}-${ARCH}.${packages_hash}.tar.lz4,其中 cache_type 根据构建目标在 climinimalxfce-desktop 或指定桌面环境名称之间切换。缓存命中时,系统通过 pv 管道将 lz4 压缩包解压到 $SDCARD,并重建 /etc/resolv.conf 与 apt 源列表;未命中则执行完整的两阶段 debootstrap,在流程末尾将制备好的根文件系统重新打包为缓存归档。缓存目录默认位于 EXTER/cache/rootfs/。当设置了 ROOT_FS_CREATE_ONLY=force 时,即使缓存存在也会强制重建。

构建模式 cache_type 取值 典型大小 tmpfs 阈值
CLI 标准版 cli ~1.5 GiB 1500 MiB
Minimal minimal < 1 GiB 1500 MiB
Desktop (xfce) xfce-desktop ~3.5 GiB 3500 GiB

内存充足时,SDCARD 目录可选择挂载为 tmpfs 以加速 I/O。构建系统根据可用物理内存与交换空间总量的 9/10 自动计算 tmpfs 大小,CLI 模式阈值约为 1500 MiB,Desktop 模式约为 3500 MiB。开发者也可通过 FORCE_TMPFS_SIZE 显式覆盖这一计算。

Sources: debootstrap.sh debootstrap.sh general.sh debootstrap.sh

Debootstrap 两阶段构建与 QEMU 仿真

当缓存未命中时,构建系统调用 Debian 官方 debootstrap 工具执行两阶段基础系统安装。第一阶段 使用 --foreign 参数将目标架构的基础包解压到 $SDCARD,此时尚未执行任何架构相关的配置脚本;第二阶段 将宿主机的 QEMU 用户态静态二进制(qemu-arm-staticqemu-aarch64-static)复制到 $SDCARD/usr/bin/,随后通过 chroot 执行 /debootstrap/debootstrap --second-stage 完成软件包配置。这种设计使得构建主机(通常为 x86_64)能够跨架构完成 ARM 根文件系统的初始化。

两阶段之间,系统还会将宿主机的 archive keyring 复制到目标目录以确保后续 apt 验证正常,并针对 sid 等滚动发行版应用特殊的快照镜像与 GPG 跳过策略。所有 debootstrap 输出均通过 EVALPIPE 数组捕获管道状态,确保即使经过 pvdialog 管道也能正确检测失败。第二阶段成功后,系统立即挂载 chroot 环境并开始 apt 源配置、locale 生成和基础包升级。

Sources: debootstrap.sh debootstrap.sh debootstrap.sh debootstrap.sh

Chroot 环境与服务管控

在根文件系统定制期间,构建系统需要反复进入 chroot 环境执行命令。image-helpers.sh 提供了 mount_chroot()umount_chroot() 两个辅助函数,负责将宿主机的 procsysdevtmpfsdevpts 挂载到目标目录,为 chroot 内的进程提供必要的内核接口和伪终端支持。构建流程结束或遭遇中断时,unmount_on_exit() 陷阱函数会递归清理所有挂载点并释放 loop 设备,同时支持在 ERROR_DEBUG_SHELL=yes 模式下进入交互式 shell 以便排查残留状态。

一个关键细节是服务启动拦截:在根文件系统构建过程中,目标系统内的 systemdsysvinit 服务绝不应实际启动。构建系统通过三重机制实现这一目标:首先写入 $SDCARD/usr/sbin/policy-rc.d 返回退出码 101 以阻止 invoke-rc.d 运行;其次使用 dpkg-divert/sbin/initctl/sbin/start-stop-daemon 替换为占位脚本;最后在 post_debootstrap_tweaks() 中恢复这些改动并移除 QEMU 二进制,确保交付的镜像具有干净的系统状态。这一拦截-清理模式对于避免构建宿主机资源被目标系统服务占用至关重要。

Sources: image-helpers.sh image-helpers.sh debootstrap.sh distributions.sh

发行版特定配置与通用系统定制

根文件系统的配置逻辑分为两个层次。install_distribution_specific() 针对特定 Debian/Ubuntu 发行版执行差异化调整,例如 Ubuntu 系列需要禁用 motd-news 服务、将 initramfs 压缩算法改回 gzip、配置 Netplan 和 systemd-resolved,以及调整 journald 的存储策略为 volatile;Debian 系列则需要移除多余的 10-uname MOTD 脚本并注入 VERSION_ID 修正。这些调整通过 case $RELEASE 结构集中管理,覆盖从 stretchnoblesid 的多个发行版本。

install_common() 则执行跨发行版的通用定制,其内容涵盖 u-boot 与内核 Deb 包的安装、root 密码与时区设置、cpufrequtils 调速策略、内核模块与黑名单写入、BSP 板级支持包安装、默认用户创建及附加组分配(sudo、docker、video 等)、串口控制台 getty 配置,以及 OrangePi 特有 systemd 服务(orangepi-firstrunorangepi-resize-filesystemorangepi-zram-configorangepi-hardware-optimizeorangepi-ramlogorangepi-hardware-monitor)的启用。此外,该函数还会根据 $REPOSITORY_INSTALL 变量决定从本地构建缓存还是远程仓库安装内核、固件和配置包,这一机制在批量构建与持续集成场景中尤为重要。最后,family_tweaks 函数若存在则会在通用定制末尾被调用,实现芯片家族级别的最终调整。

Sources: distributions.sh distributions.sh distributions.sh distributions.sh distributions.sh

用户自定义扩展与 Hook 机制

构建系统在根文件系统制备的各个阶段预留了大量 extension hook,允许用户在不修改核心脚本的前提下注入自定义逻辑。其中最具实践价值的是 customize_image() 函数:它会自动检测 userpatches/customize-image-host.sh(在宿主机执行)和 userpatches/customize-image.sh(在 chroot 内执行),并将 userpatches/overlay 目录以只读绑定挂载到 chroot 内的 /tmp/overlay,供自定义脚本引用。若 customize-image.sh 返回非零退出码,构建流程会立即终止并报告错误。

除此之外,从 pre_install_distribution_specificpost_umount_final_image,整个 debootstrap 与镜像打包链路中分布着超过 10 个 extension method 调用点。高级开发者可以通过 userpatches/extensions/ 或板卡配置中的 extension() 函数实现分区表自定义、镜像大小动态计算、格式化参数调整等深度定制。

Sources: image-helpers.sh debootstrap.sh debootstrap.sh debootstrap.sh debootstrap.sh

分区布局与镜像文件制备

prepare_partitions() 负责将空白镜像文件转化为具备分区表、文件系统和挂载点的可写入介质。镜像大小的计算遵循以下逻辑:若用户指定了 FIXED_IMAGE_SIZE,则直接使用该值并做基本的根文件系统容量校验;否则基于 $rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE,再按构建模式乘以一个经验系数(Desktop 为 1.35、CLI 为 1.30),最后向上对齐到 4 MiB 边界。空白镜像可通过 truncate 快速创建(FAST_CREATE_IMAGE=yes),也可通过 dd 完全置零以规避某些文件系统 corruption 风险。

分区表支持 msdosgpt 两种标签,由 sfdisk 写入。构建系统支持灵活的分区组合:

场景 分区结构 触发条件
无独立 boot 仅 root ROOTFS_TYPE == ext4BOOTFS_TYPE 未设置且未加密
独立 boot boot + root BOOTFS_TYPE 显式设置,或 ROOTFS_TYPE != ext4,或启用 CRYPTROOT_ENABLE
UEFI 启动 EFI + [boot] + root UEFISIZE > 0
LUKS 加密 加密 root + boot CRYPTROOT_ENABLE == yes

文件系统类型方面,根分区支持 ext4btrfsf2fsxfsnfs(网络启动),boot 分区支持 ext4ext2fatf2fsext4 在较新宿主机上会显式禁用 64bitmetadata_csum 特性以兼容旧版引导加载程序;btrfs 可启用强制压缩。分区创建完成后,loop 设备通过文件锁 /var/lock/orangepi-debootstrap-losetup 串行访问,避免多构建任务竞争。对于加密场景,cryptsetup luksFormatluksOpen 在格式化前完成,且 /etc/crypttab 会在 fstab 之前写入。

Sources: debootstrap.sh debootstrap.sh debootstrap.sh debootstrap.sh

镜像组装与最终输出

create_image() 将制备好的根文件系统内容转移到已挂载的镜像分区中。对于非 NFS 场景,系统使用 rsync -aHWXh 同步 $SDCARD/$MOUNT/,排除 /boot/dev/proc/run/tmp/sys 等伪文件系统和临时目录;随后单独处理 /boot 分区,针对 vfat 使用 -rLtWh 参数以适配 FAT32 的权限语义。同步完成后,调用 update_initramfs() 基于已安装的内核模块版本重新生成 initramfs——这一步必须在 $MOUNT 上而非 $SDCARD(可能为 tmpfs)上执行,否则 cryptsetup-initramfs 等工具会因无法访问底层块设备而异常。

U-boot 的写入通过 write_uboot() 完成:该函数从本地构建的 u-boot Deb 包中提取 platform_install.sh 脚本,调用板级特定的 write_uboot_platform 函数将 bootloader 刷写到镜像的对应偏移位置。最终,镜像文件按 ${BOARD}_${REVISION}_${DISTRIBUTION}_${RELEASE}_${IMAGE_TYPE}_linux${VERSION} 格式命名,并根据 COMPRESS_OUTPUTIMAGE 参数执行压缩、SHA256 校验和 GPG 签名。支持的压缩格式包括 gz(pigz 多线程)、xz(pixz 多线程,带内存感知限流,最多使用 8 核)和 7z。当 COMPRESS_OUTPUTIMAGE 为空时,默认输出 sha,gpg,img 三种产物。

Sources: debootstrap.sh debootstrap.sh image-helpers.sh debootstrap.sh debootstrap.sh

本地 Deb 包编译与集成

对于不在标准 Debian/Ubuntu 仓库中的额外软件包,构建系统提供了独立的 chroot 包编译环境。chroot_build_packages()EXTER/cache/buildpkg/${release}-${arch}-v${CHROOT_CACHE_VERSION} 目录中创建与目标发行版/架构一致的构建 chroot,利用 systemd-nspawndistcc 分布式编译加速。每个待编译包通过 EXTER/packages/extras-buildpkgs/*.conf 插件文件描述其源码仓库、构建依赖和产物名称,构建成功后输出到 DEB_STORAGE/extra/ 下的对应分类目录。构建 chroot 每周会自动升级一次以保持依赖新鲜。

在根文件系统装配阶段,chroot_installpackages_local() 利用 aptly 创建临时本地 APT 仓库,将 extra/${RELEASE}-desktop/extra/${RELEASE}-utils/ 目录下的 Deb 包发布到 localhost:8189,并在目标根文件系统中添加临时源列表和 Pin 优先级配置(550)。chroot 内执行 apt-get install 完成集成后,临时仓库进程被杀掉,源列表和密钥被清理,确保镜像不会遗留构建时专用的 APT 配置。这一机制使得内核模块、GPU 驱动、ROS2、Docker 等第三方组件能够以原生包管理的方式无缝嵌入最终镜像,同时支持 EXTERNAL_NEW=prebuilt 模式直接引用预编译包而无需本地重建。

Sources: chroot-buildpackages.sh chroot-buildpackages.sh chroot-buildpackages.sh

延伸阅读

根文件系统与镜像打包是 OrangePi 构建流水线的最终环节,其上游依赖包括板级配置解析、内核与 u-boot 的 Deb 包编译。建议按以下顺序深入阅读相关章节: