1. 地平线J6 BSP编译流程中的boot分区处理机制解析
在嵌入式Linux系统开发中,BSP(Board Support Package)的构建过程往往包含复杂的镜像打包流程。地平线Journey 6(J6)芯片的SDK中,pack_boot.sh脚本作为boot分区镜像生成的核心组件,其执行逻辑与分区表配置紧密相关。本文将深入分析emmc分区表中pre_cmd/post_cmd的运作机制,以及pack_boot.sh脚本的关键实现。
1.1 分区表配置中的关键指令
在J6的SDK编译体系中,out目录下的分区表JSON文件定义了存储介质布局。以emmc为例,其分区配置中两个关键指令值得关注:
json复制"boot_a": {
"pre_cmd": [
"pack_boot.sh;[ $? -ne 0 ] && exit 1;rm -rf /home/pengtianshuai/workspace/code/out/debug-gcc_12.2-64/target/deploy/rootfs/boot/System.map*"
],
"post_cmd": [
"pack_avb_img.sh boot boot"
]
}
- pre_cmd:在制作镜像前执行,通常包含环境准备和清理操作。示例中先运行pack_boot.sh生成boot所需文件,成功后删除调试符号文件System.map
- post_cmd:在镜像生成后执行,常见于签名、校验等后续处理。此处调用pack_avb_img.sh进行AVB(Android Verified Boot)签名
这种前后置命令的设计模式使得分区构建流程具有高度可扩展性,开发者可以在不修改核心脚本的情况下,通过调整JSON配置来定制化构建步骤。
1.2 AB分区设计的实现原理
J6采用了AB分区设计增强系统可靠性,这在boot_a和boot_b的配置中体现明显:
| 分区属性 | boot_a | boot_b |
|---|---|---|
| 起始扇区 | 47710208 | 110624768 |
| 大小 | 62914560 sectors (30GB) | 62914560 sectors (30GB) |
| 文件系统类型 | ext4 | ext4 |
| 依赖项 | 无 | 无 |
| GUID类型 | C12A7328-F81F-11D2-BA4B... | C12A7328-F81F-11D2-BA4B... |
AB分区的对称设计保证了OTA更新时能够无缝切换,当A分区更新失败时系统可自动回退到B分区运行。这种设计在汽车电子等对可靠性要求高的场景尤为重要。
2. pack_boot.sh脚本核心逻辑剖析
2.1 脚本执行的三阶段流程
pack_boot.sh的核心任务是为boot分区准备完整的文件系统内容,其主要执行流程可分为:
- 环境配置阶段:通过
gen_${ROOTFS_NAME}_rootfs.sh deb_config设置基础环境 - 软件包安装阶段:执行
gen_${ROOTFS_NAME}_rootfs.sh deb_install安装runtime-pkg - initramfs生成阶段:调用
gen_${ROOTFS_NAME}_rootfs.sh mk_normal_initramfs构建内存文件系统
bash复制gen_${ROOTFS_NAME}_rootfs.sh deb_config
gen_${ROOTFS_NAME}_rootfs.sh deb_install ${PRODUCT_DEB_DIR}/runtime-pkg/
gen_${ROOTFS_NAME}_rootfs.sh mk_normal_initramfs ${SRC_ROOTFS_CONFIG_DIR}/normal_ramfs_recipe_extern.json
2.2 deb_config的深度配置解析
update_j6_common_config()函数是环境配置的核心,其执行逻辑可分为多个关键模块:
网络与主机配置
bash复制# 追加hosts配置
cat <<-EOF >> "${dst_dir}"/etc/hosts
127.0.0.1 localhost
127.0.0.1 $HOBOT_HOST
EOF
# 设置静态网络接口
cat <<-EOF >> "${dst_dir}"/etc/network/interfaces
auto ${ethdev}
iface ${ethdev} inet static
pre-up ifconfig ${ethdev} hw ether ${macaddr}
address ${address}
netmask ${netmask}
gateway ${gateway}
EOF
注意事项:MAC地址和IP配置通常需要根据实际硬件环境调整,在车载系统中这些参数可能需要与T-Box等模块配合使用
系统性能调优
bash复制# CPU性能模式设置
echo 'GOVERNOR="performance"' | tee "${dst_dir}"/etc/default/cpufrequtils
# 内核日志级别调整
sed -i 's/#kernel.printk = 3 4 1 3/kernel.printk = 4 4 1 7/' "${dst_dir}"/etc/sysctl.conf
# ARP优化配置
echo "net.ipv4.conf.all.arp_ignore=1" >> "${dst_dir}"/etc/sysctl.conf
echo "net.ipv4.conf.all.arp_announce=1" >> "${dst_dir}"/etc/sysctl.conf
这些调优参数特别适合车载环境:
- CPU固定性能模式避免频率波动影响实时性
- 调整printk级别平衡日志量和性能
- ARP配置优化网络通信稳定性
首次启动标记机制
bash复制touch "${dst_dir}"/root/.not_resizefs_rootfs
touch "${dst_dir}"/root/.do_first_run
touch "${dst_dir}"/root/.not_resizefs_log
这三个标记文件控制首次启动时的特殊处理流程,如文件系统扩展、初始化脚本执行等,是嵌入式系统常见的启动优化手段。
2.3 系统服务依赖调整
J6 SDK对systemd服务依赖关系进行了精细调整,确保关键服务的启动顺序符合车载场景需求:
bash复制# 示例:调整SSH服务启动顺序
sed -i 's/After=network.target auditd.service$/After=network.target auditd.service hobot-appinit.service/' \
"${dst_dir}"/usr/lib/systemd/system/ssh.service
# NFS服务依赖调整
sed -i 's/After=rpc_pipefs.target$/After=rpc_pipefs.target hobot-appinit.service/' \
"${dst_dir}"/usr/lib/systemd/system/nfs-blkmap.service
这种调整确保了hobot-appinit(地平图自定义初始化服务)先于基础系统服务执行,为车载应用提供稳定的运行环境。
3. 构建优化与调试技巧
3.1 多版本构建策略
J6 SDK支持不同的构建模式,通过HR_TARGET_MODE变量控制:
bash复制if [ "${HR_TARGET_MODE}" = "release" ]; then
# 精简模式处理
for package in $(cat ${debian_cache_dir}/rootfs_rm_list); do
${debian_hijack_dir}/proot -0 -S ${dst_dir} \
-b ${basesys_dir}:/usr/hobot -b ${middleware_dir}:/middleware -b ${app_dir}:/app \
-b ${pkg_dir}:/mnt -q ${debian_hijack_dir}/qemu-aarch64-static \
apt remove --purge -y ${package}
done
# 禁止自动resize加速启动
sed -i 's|/usr/bin/resize > /dev/null$|#/usr/bin/resize > /dev/null|g' \
"${dst_dir}"/root/.bashrc
fi
开发模式与发布模式的区别:
- 开发模式保留调试工具和符号信息
- 发布模式会移除不必要的软件包(通过rootfs_rm_list控制)
- 发布模式优化启动速度(如禁用自动resize)
3.2 内核包的特殊处理
由于J6采用独立内核分区设计,rootfs中需要移除内核相关包:
bash复制linux_pkg=$(${debian_hijack_dir}/proot -0 -R ${dst_dir} \
dpkg -l | grep linux-image | cut -d ' ' -f 3)
if [ -n "${linux_pkg}" ]; then
${debian_hijack_dir}/proot -0 -R ${dst_dir} \
dpkg --purge ${linux_pkg}
fi
常见问题:若忘记移除内核包可能导致系统尝试从错误位置加载内核,引发启动失败
3.3 日志系统配置优化
针对车载系统存储限制,对journald进行了专门配置:
bash复制{
echo "Storage=auto"
echo "SystemMaxUse=100M"
echo "RuntimeMaxUse=100M"
echo "ReadKMsg=no"
echo "Compress=no"
echo "MaxFileSec=0"
echo "SystemMaxFileSize=2M"
echo "SystemMaxFiles=1000"
} >> "${dst_dir}"/etc/systemd/journald.conf
这些参数控制日志系统:
- 限制日志总大小不超过100MB
- 禁用压缩减少CPU开销
- 控制单个日志文件大小(2MB)和数量(1000个)
4. 高级调试与问题排查
4.1 典型构建失败场景分析
问题1:pre_cmd执行失败
- 现象:构建在pack_boot.sh阶段中断
- 排查步骤:
- 检查out/target/deploy/rootfs/boot目录权限
- 确认SDK环境变量设置正确(如TARGET_DEPLOY_DIR)
- 查看构建日志中的具体错误代码
问题2:AVB签名失败
- 现象:post_cmd阶段的pack_avb_img.sh报错
- 解决方案:
- 确认签名证书路径正确
- 检查boot分区大小是否足够包含签名数据
- 验证avbtool版本与SDK要求匹配
4.2 性能调优建议
对于车载系统,建议额外调整以下参数:
bash复制# 在update_j6_common_config函数中添加
# 减少swap使用倾向
echo "vm.swappiness=10" >> "${dst_dir}"/etc/sysctl.conf
# 增加文件系统缓存压力阈值
echo "vm.vfs_cache_pressure=50" >> "${dst_dir}"/etc/sysctl.conf
# 调整dirty page回写阈值
echo "vm.dirty_ratio=10" >> "${dst_dir}"/etc/sysctl.conf
echo "vm.dirty_background_ratio=5" >> "${dst_dir}"/etc/sysctl.conf
4.3 多用户支持实现
SDK通过multiuser开关控制用户体系:
bash复制if [ "${multiuser}" = "y" ]; then
# 创建horizon用户并设置密码
${debian_hijack_dir}/proot -S ${dst_dir} \
adduser --quiet --disabled-password --shell /bin/bash horizon
# 修改串口自动登录用户
sed -i 's/--autologin root/--autologin horizon/' \
"${dst_dir}/lib/systemd/system/serial-getty@ttyS0.service"
fi
在实际车载项目中,多用户模式可用于区分:
- 系统管理用户(root)
- 应用运行用户(horizon)
- 诊断用户(diag)
这种隔离增强了系统安全性,符合ISO 21434等汽车网络安全标准的要求。
