资讯动态

openEuler上构建AGL车载系统:容器化Yocto与QEMU验证全记录

发布时间:2026/10/21 17:46:25 来源:尧图企业网站定制
做了多年嵌入式Linux开发我一直对车载场景的系统栈有浓厚兴趣。前阵子手头正好有一台跑着openEuler的机器想着能不能直接在它上面把面向汽车场景的操作系统AGLAutomotive Grade Linux完整部署起来而不是像大多数人那样老老实实去装个别的发行版再折腾。实测下来这条路完全走得通只是有几个坑必须先避开。这篇文章就是把这套在openEuler环境上从零构建AGL、再用QEMU把镜像跑起来的完整过程记录下来顺便把选型思路、踩坑经历和后续应用定制方向都聊透。如果你正在做车载信息娱乐系统、仪表盘、后排娱乐这类项目或者只是想在嵌入式Linux和Yocto构建上积累实战经验这篇文章应该能帮你省下大量试错时间。我会先解释为什么要在openEuler上做这件事再给出我验证过的部署路径和完整步骤最后展开构建和运行阶段最常见的几个问题以及AGL应用层的基本玩法。1. 在 openEuler 上折腾 AGL先想清楚这两件事1.1 AGL 到底解决了车载开发的什么痛点车载系统开发和普通嵌入式Linux开发最大的区别在于它对稳定性、启动时间、多屏交互和长期维护都有远超一般场景的要求。过去很多车厂的做法是从某个内核版本出发自己维护一个内核、一套驱动、一堆系统服务再和各供应商的闭源SDK做集成。这种模式开发周期长、人员依赖重而且每一款车型的系统基础都很难复用最终大部分成本都耗在了重复的移植和适配上面。AGL正是冲着这个问题来的。它不是一个简单的内核加根文件系统而是一整套面向汽车场景的应用平台底层用Yocto Project管理整个构建体系中间层提供Wayland/Weston合成器、应用生命周期管理、安全策略和车内总线访问控制上层则定义了一套标准的Application Framework简称AFB让开发者可以像写普通应用程序一样去开发车载功能而不是每次都要面对内核和驱动。对我来说AGL最大的价值在于它把车厂需要但没必要自己造一遍的东西都提前做好了。比如多屏幕显示、硬件抽象、权限管理、OTA升级框架这类模块AGL社区已经有相对成熟的参考实现。做车载软件的人拿到的不是一个教学Demo而是一个按工业化思路组织好的项目基线。1.2 为什么宿主选 openEuler 完全可行openEuler是面向服务器、云、嵌入式等多场景的开源Linux发行版本身拥有比较完整的包管理体系和活跃的社区。很多人的第一反应是AGL官方文档默认的构建宿主通常是一些常见的桌面发行版openEuler这种偏向服务器场景的系统能行吗我的实测结论是完全能行但要做一层隔离。AGL真正的构建核心是Yocto/bitbake它对你宿主发行版类型的敏感度主要集中在一组构建依赖gcc、make、Python、chrpath、diffstat等上而这些依赖在openEuler的软件源里几乎全部能找到。真正的风险在于AGL的配置脚本比如aglsetup.sh并没有对openEuler做专门的适配某些路径判断和包名检查可能在RPM系发行版上有偏差。所以更稳妥的思路是让openEuler承担资源提供者的角色把真正的构建环境放到容器里。这个思路其实很符合openEuler的气质。它自带的Podman/Docker体系本身就很成熟用来承载独立的构建容器非常顺再加上KVM虚拟化支持良好用QEMU跑AGL镜像也几乎没有性能损失。整条链路的逻辑是openEuler提供容器运行环境和KVM加速能力构建任务在Debian系容器里完成运行验证在QEMU虚拟机里完成。这样既绕开了宿主适配问题又保住了openEuler统一管理开发机环境的优势。2. 部署路径选型构建和运行必须分离2.1 原生构建的兼容性成本刚开始我确实尝试过直接在openEuler宿主上跑AGL的全套构建脚本。毕竟如果一条命令能解决何必多套一层容器。但很快就发现问题AGL的快速开始脚本在设计时主要针对Ubuntu等系统做过充分验证里面有一些对包管理器乃至特定版本的隐性依赖。openEuler用的是RPM系虽然也能通过dnf装齐大部分工具但某些AGL依赖的Perl模块、Python包的安装方式会不一样。更尴尬的是Yocto构建过程中会执行大量编译任务这些任务依赖的宿主工具版本必须匹配。如果某个工具的版本偏新或偏旧可能导致莫名其妙的编译错误而且这种错误往往不是来自你的代码而是来自构建环境本身。与其花时间逐个排查不如直接在容器里准备一个AGL官方推荐类型的系统。2.2 容器化构建隔离依赖的最佳选择容器的核心价值在于把构建环境的复杂性和宿主系统的维护责任分开。你不需要为了AGL去改装自己的openEuler工作机也不需要担心多个项目之间依赖冲突。我最后选的是Debian系的容器镜像因为它和Yocto文档中常见支持的发行版血缘最近包名和版本基本一致能够最大程度降低环境差异带来的噪音。在容器里做构建还有一个间接好处构建过程中的临时文件、下载缓存、编译产物全部留在容器的挂载目录里宿主始终是干净的。如果哪次构建把环境弄坏了直接删掉容器重建一个就行完全不污染主系统。这种坏了就扔的心态能让整个验证过程压力小很多。2.3 QEMU 虚拟化面向运行态验证构建出镜像只完成了一半。AGL是个实实在在的操作系统你得真正把它启动起来看Weston合成器是否正常拉起看应用能否通过Application Framework完成通信看触摸和显示是否流畅才算把部署这件事闭环了。在开发机上最可控的方式就是用QEMU跑模拟器镜像。QEMU本身在openEuler上可以直接通过dnf安装只要你开启了KVM支持x86_64镜像的运行速度非常接近真机。这一步的好处是不需要准备任何真实开发板不需要烧录SD卡所有操作都在命令行里完成。尤其在做快速功能验证时构建一个新镜像然后一键启动的效率是开发板烧录完全没法比的。2.4 最终技术方案综合下来我最终确定的部署链路是这样的环节方案理由宿主系统openEuler统一开发机环境容器和虚拟化支持好构建环境Debian系容器规避aglsetup.sh对宿主发行版的兼容性检查镜像格式wic/xzAGL默认产出适合QEMU直接启动运行验证QEMU KVM本地快速启动适合反复迭代后续定制AFB widget 本地构建与应用框架衔接后续可无缝迁移到真实硬件这几层各司其职后面所有步骤都是围绕这个方案展开的。如果你打算在openEuler的服务器上做车载软件CI流水线这套架构同样适用只要把容器构建和QEMU冒烟测试接入到Jenkins或GitLab CI即可。3. 完整实操从空 openEuler 到 AGL 镜像跑起来3.1 宿主机准备与资源评估在开始一切操作前先评估资源。AGL构建是一个重活完整编译agl-demo-platform至少需要100GB左右的可用磁盘空间内存建议16GB以上低于8GB会非常痛苦。CPU方面多核有帮助bitbake默认会按核数并行。下面的命令在openEuler宿主机上执行# 更新软件源并安装基础工具 sudo dnf update -y sudo dnf install -y podman docker qemu-kvm libvirt virt-install \ git wget tar xz unzip python3 # 确认KVM可用输出带 vmx/svm 标志即可 grep -E vmx|svm /proc/cpuinfo # 启动并配置容器服务 sudo systemctl enable --now podman.socket sudo systemctl enable --now docker sudo usermod -aG docker $USER注意如果你用的是Podman注意容器内普通用户的UID/GID和宿主机是否一致这关系到后面共享目录的权限问题。我会在第4节详细展开。磁盘空间我建议分两个区理解一个是容器镜像和构建缓存区至少60GB另一个是AGL代码和构建产物区至少40GB。如果是同一块盘总量100GB即可。网络方面AGL构建过程会从上游拉取很多开源软件源码一个稳定且能访问各开源站点的网络环境是硬性要求建议提前配置好合适的系统软件源镜像减少宿主机层面的下载异常。3.2 构建容器搭建与依赖安装我选择了Debian 12作为容器基础镜像原因前面说过离Yocto文档推荐的发行版最近。搭建容器时把宿主机上的一个工作目录挂载进去这样源码、构建产物都能保留在宿主机上即使容器重建也不丢数据。mkdir -p ~/agl-work sudo chown $USER:$USER ~/agl-work docker run -itd --name agl-build \ -v ~/agl-work:/home/agl/work \ debian:12 bash docker exec -it agl-build bash容器启动后先创建普通用户因为Yocto明确禁止用root执行bitbake。然后安装构建所需的依赖包# 在容器内执行 apt-get update apt-get install -y gawk wget git-core diffstat unzip texinfo \ gcc-multilib build-essential chrpath socat cpio python3 \ python3-pip python3-pexpect xz-utils debianutils iputils-ping \ python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev pylint3 \ xterm repo # 创建构建用户并切换 useradd -m -s /bin/bash agl su - agl这里面有几个包值得多说一句。chrpath负责在编译后修改ELF文件里的rpathYocto的打包逻辑依赖它缺失会导致do_package阶段报错。libsdl1.2-dev则和QEMU图形输出相关如果你后面只在无头模式下跑可以省掉但一次性装齐最省事。3.3 拉取 AGL 源码与切换分支AGL的源码是用Google的repo工具管理的。整个代码量相当大包含Yocto的meta层和各类应用模块直接在挂载目录里操作# 在容器内以agl用户操作 cd ~/work repo init -u https://.../AGL/AGL-repo.git -b release分支 -m manifest文件名 repo sync具体的分支名和manifest文件名随AGL版本发布变化原则是选择官方长期支持的release分支。repo sync的输出非常长首次同步建议不要中断。这个环节最容易遇到的问题就是某个仓库下载失败后续我会讲怎么用repo sync -j参数和缓存策略减少失败概率。同步完成后的目录结构是这样的顶层是一堆meta-agl开头的layer目录外加一个build目录通常是空的或者不存在需要后续生成。Yocto的layer机制让你可以方便地叠加自己的定制层这也是后面做应用定制的基础。3.4 配置机器与特性并开始 bitbakeAGL提供了一套配置脚本叫aglsetup.sh。它负责生成Yocto所需的local.conf、bblayers.conf等配置文件并允许你指定目标机器、构建目录和需要启用的功能特性。# 在代码根目录执行 source meta-agl/scripts/aglsetup.sh \ -m qemux86-64 \ -b agl-demo \ agl-demo agl-netboot agl-appfw-smack这里的参数含义是-m qemux86-64目标机器为QEMU的x86_64模拟器AGL针对它有专门的BSP支持。-b agl-demo指定构建目录名产物会生成在这个目录下。agl-demo启用demo特性会把一组演示应用和Weston配置带进来。agl-netboot启用网络相关组件方便启动后通过SSH访问。agl-appfw-smack启用AGL应用框架并打开Smack安全模块。构建目标这里选择的是agl-demo-platform它是最接近完整车载系统体验的镜像包含桌面、多屏示例和一些基本车载应用cd ~/work/agl-demo bitbake agl-demo-platform第一次构建的时间取决于你的CPU和网络。我这边8核16线程的机器网络稳定的情况下大概花了3到4个小时。bitbake的日志冗长且琐碎建议打开一个新终端定期检查磁盘空间和编译日志的尾部。3.5 产物解析与 QEMU 启动验证构建完成后镜像会出现在构建目录下的tmp/deploy/images/qemux86-64/中。重点关注的几个产物产物作用.wic.xz压缩后的完整系统镜像可直接写入磁盘也可被QEMU引导.ext4根文件系统镜像适合手动挂载检查或做容器基础镜像modules.tar.gz内核模块归档交叉编译外部驱动时非常有用bzImage或Image内核镜像本体直接用AGL提供的脚本启动最稳妥cd ~/work/agl-demo/tmp/deploy/images/qemux86-64 runqemu agl-demo-platform wic kvm脚本会自动检测KVM、选择合适的QEMU参数并把网络、显示都配好。如果一切正常你会看到内核日志快速滚动然后Weston启动最终显示出一个带桌面图标的AGL demo界面。如果显示不正常先别急着怀疑构建参看第4节我列出的排查项。4. 构建期真实踩坑记录三条最容易翻车的链路4.1 磁盘耗尽与共享目录权限第一次构建我在半夜醒来发现bitbake报错提示No space left on device。检查之后发现根因不是整体磁盘满了而是我挂载的目录在容器里是root占有的agl用户在往里面写入缓存时因为磁盘配额和文件系统预留空间的问题导致提前触顶。解决方案分两层。第一层是权限在容器启动时使用-u参数指定用户或者干脆在挂载目录上执行chown让容器内用户可写。第二层是预留空间df -h看到的是名义空间ext4文件系统默认给root预留5%的空间普通用户在接近满盘时会先撞墙。对于构建目录这种大文件密集区我建议预留出20%的余量或者把构建缓存目录指向另一个独立分区。4.2 网络下载失败与缓存复用Yocto构建的本质是下载源码 - 编译 - 打包下载是高频失败点。AGL依赖的源码包分布在不同站点有些下载速度不稳定偶尔会超时。我的处理方法是多管齐下先给repo换源或增加重试。repo sync可以用repo sync -j4降低并发数减少单次连接数过多导致的封禁或超时。更重要的是让Yocto的下载缓存落盘复用。bitbake的downloads目录默认在构建目录里误删或缓存路径漂移会让下一次构建重新下载一遍所有代码非常伤。我习惯把下载缓存独立出来通过DL_DIR指向一个固定目录# 在 local.conf 中追加 DL_DIR /home/agl/work/downloads SSTATE_DIR /home/agl/work/sstate-cache这样即使整个构建目录删除缓存和编译中间态还能复用下次构建至少能省掉一半时间。这个经验在容器重建时尤其有用。4.3 SELinux 拦截容器输出的奇怪现象openEuler默认启用了SELinux多数情况下它不会影响容器内的编译但偶尔会拦截容器对宿主机挂载目录的写入或者阻止QEMU访问KVM设备节点。表现是编译刚开始一切正常过一段时间突然报权限错误而且错误信息里看不到任何和SELinux相关的上下文。排查思路是先看dmesg有没有AVC拒绝记录再用audit2why定位具体的策略。sudo dmesg | grep -i avc | tail -20如果确认是SELinux拦截最常见的做法是把相关目录的策略调整好而不是直接关闭SELinux。实际开发机上如果只做构建验证临时设置为宽松模式也可以快速确认问题sudo setenforce 0但我个人的经验是找到具体被拒绝的文件类型执行chcon -t container_file_t -R ~/agl-work比整体关闭SELinux更符合openEuler的维护习惯。毕竟你不想为了省事把一个安全机制彻底关掉。5. 跑通之后还能做什么AGL 应用层定制5.1 AGL Application Framework 的组成AGL真正区别于普通嵌入式Linux的地方是它的应用框架。理解了这个框架做一个车载应用就变成了一件有明确路线的工程任务而不是从零开始去写系统服务。AGL的Application Framework以afb-daemon为核心进程它负责管理应用的注册、启停、IPC和权限。应用本身被打包成widget文件内部可以包含HTML5前端、原生可执行文件或C/C服务端binding。binding是可以被afb-daemon加载的动态库通过JSON-RPC与前端交互。这套模型带来的好处是前后端分离界面用Web技术实现业务逻辑用C/C实现两者通过统一的API通信。对团队分工来说UI工程师和底层工程师可以并行开发不再被语言绑架。5.2 快速添加一个自研 widget一个最小的AGL widget并不复杂。你需要准备一个包含config.xml的widget包里面描述应用名称、版本、入口点然后把可执行文件或HTML文件放进去。下面是一个非常简单的示例目标是在桌面上显示一段文字mkdir -p my-widget/usr/lib/myapp cat my-widget/config.xml EOF ?xml version1.0 encodingUTF-8? widget idmyapp version1.0 nameMy First App/name content srcindex.html/ /widget EOF cat my-widget/usr/lib/myapp/index.html EOF html body h1Hello AGL/h1 /body /html EOF然后把它打成wgt包通过SSH传到目标系统里用afm-util安装zip -r myapp.wgt config.xml usr/ scp myapp.wgt rootAGL_IP:/root/ # 在AGL目标机执行 afm-util install /root/myapp.wgt afm-util start myapp1.0执行完这两个命令应用就会出现在桌面上。这个过程很直接但第一次做建议拆成两步走——先确保安装成功再启动并观察journalctl -u afb-daemon里的日志。这和Linux系统调试的思路一致日志总是比现象更早暴露问题。5.3 运行时安装与镜像集成两条路径widget的安装方式有两条路径。上面演示的是运行时安装适合开发和测试阶段。另一条路径是在Yocto构建时把应用作为image feature集成进去具体操作是创建一个自己的meta层在image recipe里加入对widget的依赖。这条路径的优点是产品化程度高、可复现性强适合做版本发布。我在实际项目中更倾向于混合使用开发阶段用运行时安装快速迭代每次CI构建时再跑一次镜像集成把冒烟测试过的widget固化进完整镜像。这样既保证了开发效率又保证了产物的稳定性和可追溯性。6. 跑完整个流程后我个人的几点体会整套环境跑通之后我对车载操作系统部署这件事有了一个更具体的判断。AGL确实可以做到一个镜像多种车型复用的愿景但前提是团队的构建能力和应用框架理解要达到一定水平。它不像普通桌面系统一样装上就能用需要的是系统集成能力、软件包管理意识和对Wayland/Smack这类底层组件的把握。最后分享一个小技巧在openEuler上维护AGL构建环境时给容器起名时加上构建目标和日期比如agl-debian12-0715。你会很快发现随着版本迭代和层变更构建容器和环境会多到记不住。这套命名习惯让我在同时维护多个AGL版本时几乎没有找错过环境也方便我随时丢到一个临时容器做隔离验证。车载软件的路还很漫长但如果开发环境的可复现性能做到这个程度后续的交付和迭代都会省心不少。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑