4.4.2. 集成用户软件进系统镜像

在开发过程中,常常需要将一些配置文件或开发完成的程序直接包含到系统镜像中,以便在设备运行时能够在板端直接使用这些文件或程序。本章将详细介绍两种常用的方法,将用户的软件和文件集成到系统镜像中:

  1. 将文件添加到根文件系统的 system 分区

    这种方式适用于需要直接与系统文件一同部署的文件,例如系统库、系统配置文件等。通过将文件放入 system 分区,可以确保它们在设备启动时即可被加载和使用。

  2. 新增一个独立的分区存放自定义文件

    通过创建新的分区并将自定义文件、程序存放其中,可以实现更高的灵活性和模块化。新分区会生成独立的镜像文件,方便管理和更新,同时也能避免对根文件系统的影响。

这两种方法各有特点,可根据需求选择合适的集成方式来优化开发和部署流程。以下将分别介绍两种方法的使用。

4.4.2.1. 将文件添加到根文件系统的 system 分区

将用户自定义文件集成到根文件系统非常简单。只需将准备好的文件复制到路径 system/buildroot/prebuilt/boot-utils-runtime 下。在编译 system 镜像时,这个目录中的所有内容都会被自动打包到 system 分区中,无需额外操作。合并这部分文件的编译代码在 BSP 源码的 build/mk_system.sh 脚本中,实现代码如下:

function build_unpack()
{
        ... ( 省略 ) ...

        # 合并自动启动配置项
        if [ -d "${SYSTEM_ORIG_DIR}/boot-utils-runtime" ]; then
                rm -rf "${SYSTEM_BUILD_DIR}"/etc/dropbear
                cp -arf --remove-destination ${SYSTEM_ORIG_DIR}/boot-utils-runtime/* "${SYSTEM_BUILD_DIR}"
        fi
}

注意事项:

  • system 分区的容量具体大小可能因软件版本有所不同。如果添加的文件导致分区总大小超过预设容量,镜像生成将失败。为避免此问题,可以通过修改分区表来调整 system 分区的大小。详细方法请参考分区配置

在操作前,请确认 system 分区容量限制,并确保添加的文件不会超出可用空间。

4.4.2.2. 新增独立分区存放自定义文件

为更灵活地管理程序或配置文件,可以通过新增一个独立分区专门存放这些内容。这种方式特别适合需要对程序进行独立编译、管理和升级的场景。本节以配置 app 分区为例,详细说明操作步骤(app 分区默认已支持)。

注意事项

  1. 关于分区表最后一个分区

    如果新增的分区位于分区表的最后一个分区之后(例如在 userdata 分区之后),需要注意以下行为:

    • 系统启动时,Uboot 阶段会根据硬件存储设备(如 eMMC 或 NAND Flash)的实际容量,自动将最后一个分区扩展至存储设备的末尾,以最大化利用存储空间。

    • 首次启动时,Kernel 检测到最后一个分区未被格式化或没有有效的文件系统时,会自动执行格式化操作。

  2. userdata 分区默认设置

    • 系统镜像制作时,userdata 分区通常是最后一个分区,不包含数据,且分区大小设置较小(默认 50MB),以减小镜像体积。

    • 启动后,Uboot 会自动扩展 userdata 分区到剩余存储空间,首次进入系统时会格式化该分区。

    • 如果在 userdata 之后新增分区,请调整 userdata 的大小以满足实际需求。

  3. 关于空分区

    • 如果新增分区在镜像制作时为空(无有效数据,如 userdataprivate),需要在系统首次启动时进行格式化。

    • 格式化逻辑可以参考 /etc/init.d/S65mountall 脚本,将新分区名添加到 force_format_part_list 变量中,例如:

      force_format_part_list="app userdata log private"
      

实现步骤

1. 修改分区表

在分区表中定义新分区。以 device/horizon/x5/board_cfg/soc/x5-soc-debug-gpt.json 为例,添加如下配置:

"app": {
    "fs_type": "ext4",
    "part_type": "GOLDEN",
    "size": "700m"
},
  • fs_type:分区文件系统类型,设置为 ext4

  • size:分区大小,单位为 MB(示例为 700m)。

有关分区表配置的更多信息,请参考分区配置

2. 准备分区内容

创建一个用于存放分区文件的目录(如 app),并在其中添加需要的文件。例如,添加一个名为 startup.sh 的启动脚本:

# 在 BSP 源码根目录下执行以下命令
mkdir app
cd app
echo "#!/bin/sh" > startup.sh
echo 'echo "This is the test code"' >> startup.sh
chmod 777 startup.sh

若新增分区为空,此步骤可跳过。当前系统中 /app 和 /userdata  目录下的 startup.sh 程序会被 /etc/init.d/S99auto_startup 服务在系统启动时调用执行。

3. 验证生成分区镜像

参考以下编译程序,根据分区目录内容生成镜像,例如:

./build/mk_app.sh

生成的镜像文件位于 out/product/app.img。若新增分区为空,可跳过此步骤。

4. 集成编译脚本

xbuild.sh 中添加分区编译逻辑:

  • avail_func 中添加分区选项:

    avail_func=("all" "lunch" "miniboot" "uboot" "factory" "boot" "hbre" "system" "app" "pack" "otapackage")
    
  • 定义 build_app 函数:

    function build_app {
        is_exist=$(get_part_exist app)
        if [ "$is_exist" = "0" ]; then
            return
        fi
        build_component "app" "${HR_LOCAL_DIR}/mk_app.sh" "$@"
    }
    
  • 在整体编译逻辑中加入 app 分区:

    if [ -d "${HR_TOP_DIR}/app" ]; then
        build_app "$opt"
    fi
    

以上代码添加完成后,可通过 ./bd.sh app./mk_app.sh 编译生成 app.img

5. 自动挂载新分区

在系统中添加新分区后,为确保分区能在启动时自动挂载,需要在 system/buildroot/prebuilt/boot-utils-runtime/etc/hb-fstab 文件中添加对应的挂载配置。例如,新增 app 分区的挂载配置如下:

/dev/block/platform/by-name/app  /app  ext4  defaults  0  1

以下是各字段的含义及相关注意事项:

  • 设备路径 (/dev/block/platform/by-name/app)

    • 指定分区设备的路径。

    • 根据设备和分区配置,此路径可能有所不同,请确保与实际设备路径一致。

  • 挂载点 (/app)

    • 指定分区挂载的目标目录。

    • 系统启动时,分区内容将挂载到此目录下。

    • 需要确保该目录已在镜像制作过程中创建,否则系统将无法挂载分区,可能导致启动失败。例如在 mk_system.shbuild_unpack 函数中添加目录创建逻辑:

      mkdir -p ${SYSTEM_BUILD_DIR}/{app,log,userdata,usr/hobot,data,private}
      
  • 文件系统类型 (ext4)

    • 指定分区的文件系统类型。

    • 根据分区的实际文件系统格式选择合适的值(如 ext4vfat 等)。

    • 此处必须与分区表中配置的 fs_type 一致。

  • 挂载选项 (defaults)

    • 使用默认挂载选项。defaults是一个预定义的选项集合,它包括了以下特性:

      • rw(读写模式):允许对分区进行读写操作。

      • suid(允许设置用户 ID):允许执行文件的 SUID(Set User ID)和 SGID(Set Group ID)权限。这允许某些程序在执行时以文件所有者的权限运行。

      • dev(允许设备文件):允许分区中存在设备文件(如/dev目录中的文件)。

      • exec(允许执行文件):允许在分区中执行文件。

      • auto(自动挂载):允许分区在系统启动时自动挂载。

      • nouser(不允许普通用户挂载):只有 root 用户或具有特定权限的用户才能挂载该分区。

      • async(异步 I/O):允许异步 I/O 操作,提高性能。

    • app分区的挂载配置中,defaults选项的具体含义如下:

      • 分区设备/dev/block/platform/by-name/app将以读写模式挂载到挂载点/app

      • 分区中的文件可以设置 SUID/SGID 权限,以支持特定程序的权限需求。

      • 分区中的文件可以被系统执行,确保应用程序的正常运行。

      • 分区将在系统启动时自动挂载,无需手动干预。

      • 只有 root 用户或具有特定权限的用户可以挂载该分区,以增强系统的安全性。

      • I/O 操作将以异步方式执行,从而提高分区的性能表现。

    • defaults 选项可以确保分区在挂载时具备基本的读写和执行权限。

    • 如果需要自定义挂载参数,可根据需求替换为实际参数。例如:

      • ro:只读挂载。

      • rw:读写挂载(默认)。

      • noexec:禁止执行分区中的二进制文件。

      • nosuid:禁止设置 SUID 和 SGID 标志位。

  • 备份标志 (0)

    • 指定该分区是否需要 dump 备份。

    • 设置为 0 表示不备份,是通常设置为 0

  • 文件系统检查顺序 (1)

    • 指定系统启动时检查文件系统的顺序。

    • 0 表示不检查该分区。

    • 非零值表示检查顺序,数字越小,优先级越高。

6. 打包分区进系统镜像

完成所有配置后,可以通过编译脚本生成分区镜像,并把新增的分区镜像 pack 到系统镜像中。

使用以下命令执行完整编译和打包流程:

./bd.sh

如果新增的分区是空分区(即在镜像生成时未包含实际数据,如 userdata 分区),可以选择跳过该分区的镜像打包操作。在 xbuild.sh 脚本的 truncate_fill_image 函数中,可以按如下方式调整逻辑:

# FIXME: If there is actual data in the partition behind the mirror, pack will be skipped.
# 先固定跳过 log 和 userdata 分区,要优化成根据配置来找到最后一个有数据的分区
case "${part_name}" in
    log*|userdata*|app*)
        rm -f "${HR_TARGET_PRODUCT_DIR}/${part_name}".img
        ;;
esac

完成以上步骤后,新增的分区(如 app 分区)将被编译并合并到系统镜像中。 如需验证,可以检查镜像文件 out/product/app.img 是否存在,并通过刷机或启动系统验证新增分区的功能是否正常。

验证

完成刷机后:

  • 系统启动时如果用户通过串口连接设备,那么可以在串口的打印日志上看到 This is the test code 的日志输出,说明 /app/startup.sh 已经运行。

  • 使用 fdisk -l 查看分区表,确认 app 分区存在,大小为 700MB(分区大小需要和实际的分区表配置对齐)。

  • 使用 mount 确认分区已挂载至 /app

  • 使用 ls /app 查看分区内容,确认 startup.sh 存在并可执行。

    示例输出:

    # ls /app/
    startup.sh  lost+found