做 OpenHarmony 的人,应该都有过这种体验:照着教程把源码拉下来,然后面对几十个 G 的目录树,根本不知道该从哪看起。我最早接触 OpenHarmony 的时候,盯着顶层目录发了半天呆——代码量太大,子系统太多,ArkUI、分布式软总线、HDF 驱动框架这些名词堆在一起,就像拿到了一本没有目录的百科全书。
后来跟着编译、改板卡、看启动日志,踩了无数坑,才慢慢把源码树这张“城市地图”读懂。这篇文章就想把这张地图的关键路径画给你看,同时结合我在 RK3568 和 x86 平台上实际折腾的经验,把“设备树到底怎么选”“x86 版能不能在电脑上跑”这些高频问题一次讲透。
如果你是第一次接触 OpenHarmony,正被源码树劝退;如果你已经在 RK3568 板子上跑过 demo,但面对一堆 dts 文件不知道怎么选;如果你还想在电脑上试着跑一个 x86 版 OpenHarmony——这篇文章应该是你需要的。
1. 源码树不是一堆代码,而是一张城市地图
1.1 OpenHarmony 到底有多大,为什么需要“解剖”
OpenHarmony 不是一个单一仓库,而是几十个 git 仓库通过 repo 工具组织起来的超大规模工程。release 版本的完整源码加上编译工具链和预编译产物,轻轻松松超过 20GB,编译之后 out 目录再占几十 GB。这个体量决定了你不可能“一行行看完”,必须带着目的去解剖。
我见过太多新手犯同一个错误:想先浏览一遍源码再开始动手,结果浏览了两个星期最后还是回到编译这一步。正确的做法恰恰相反,先让系统跑起来,再顺着“我要改什么”“我想看什么”反向去源码树里找对应位置。
源码树本质上是一张城市地图:顶层目录就是各个功能区,比如市区、工业园、轨道交通枢纽。你要找某个系统能力、某块驱动、某个产品配置,如果不知道它在哪个区,找起来就是大海捞针。所以“解剖源码树”的核心不是背目录,而是建立一套从问题到路径的映射关系。
1.2 顶层目录大盘点:一眼认出每个仓库的职责
把 OpenHarmony 顶层目录按功能分类,可以整理成下面这张简表,建议第一次接触时先记住这些分类,剩下的细节需要时再查。
| 目录 | 职责 | 一句话理解 |
|---|---|---|
| foundation | 系统基础能力 | 元能力、ArkUI、通信、媒体等子系统的核心代码 |
| base | 基础公共模块 | 安全、启动恢复、全局资源等底层公共能力 |
| device | 设备适配 | 开发板、SoC、外设相关的适配代码 |
| vendor | 厂商产品 | 各硬件厂商的具体产品形态和配置样例 |
| productdefine | 产品定义 | 定义编译出的产品有哪些特性和模块 |
| kernel | 内核 | Linux 内核、LiteOS 等内核代码 |
| drivers | 驱动框架 | HDF 驱动框架及驱动实现 |
| ark | 方舟运行时 | 方舟编译器与 JS 运行时相关 |
| build | 编译构建 | hb 工具、编译脚本、构建模板 |
| third_party | 三方开源库 | 用到的社区开源组件,如 zlib、mbedtls |
| interface | 接口定义 | 对外 API 的 IDL 和头文件 |
| applications | 系统应用 | 桌面、设置、相机等预置应用 |
| test | 测试套件 | 各子系统的测试代码 |
| utils | 公共工具库 | 日志、错误码等轻量工具 |
| developtools | 开发工具 | 调测工具、SDK 工具链等 |
这套顶层结构里,最容易让人迷茫的是 foundation 和 base 这两个目录。简单说,foundation 是“功能核心”,像元能力(Ability)、ArkUI、分布式数据管理都在里面;base 是“地基工具”,负责安全、启动恢复、资源调度这些偏系统底层的职责。前者管“系统能干什么”,后者管“系统怎么稳定跑起来”。
2. 核心目录拆解:系统能力都藏在 foundation 和 base 里
2.1 foundation 里的子系统是怎么划分的
进入 foundation 目录,你会看到很多子目录,每个子目录基本对应一个子系统或一个关键能力。比如 foundation/ability 是元能力框架,负责应用怎么启动、页面怎么跳转;foundation/arkui 是 UI 框架,你写的 XML 或者 ArkTS 声明式界面最终都由它渲染;foundation/communication 是分布式通信,Wi-Fi、蓝牙、软总线都在这一带。
这里我建议新手重点看 foundation/ability、foundation/arkui、foundation/communication 三个目录。从应用开发的角度来说,Ability 是打开应用的基础,ArkUI 是写界面的基础,通信是 OpenHarmony“万物互联”招牌的根基。把这三个目录的顶层结构读明白,你对整个系统的运行逻辑会有一个质的提升。
每个子系统内部通常还分接口层、框架层和服务层。接口层给应用开发者暴露 API,框架层实现核心逻辑,服务层跑在系统进程里提供跨进程能力。这个分层思路和很多大型操作系统是一致的,理解了它,再去查具体代码会快很多。
2.2 base、interface、common 这些“小目录”为什么重要
有相当一部分人拉完源码,根本不会打开 base 目录一眼,但其实它决定了一个系统“能不能稳”。base/security 管权限和安全策略,base/startup 管开机启动流程,base/global 管全球化资源。如果你要做系统级定制,比如修改开机时长、调整权限管控策略,答案基本都在 base 里。
interface 目录看着不起眼,但它定义了系统对外的 API 契约。很多跨子系统调用都是通过 interface 里定义的接口来对接的,改接口名、改参数结构,往往会引起连锁编译错误。我在二开时有个习惯:要动某个子系统的接口,先去 interface 目录查有没有对应的声明,避免只改实现不改契约导致其他模块编译不过。
还有 common 这类容易被忽略的目录,里面通常是错误码定义、公共日志工具等“杂项”。虽然单个文件不大,但几乎所有子系统都会依赖它们,编译报错如果找不到某个通用的基础头文件,很多时候就是 common 目录没有拉取完整或者版本不匹配导致的。
3. 拿到 RK3568 先别急着编译:设备树到底怎么选
3.1 RK3568 板子为什么会有十几个设备树
说到 RK3568,就绕不开设备树这件事。很多人在 OpenHarmony 源码里一搜rk3568,会发现 device 和 vendor 目录下躺着很多 dts 文件,加上内核里带的一堆 dtsi,看起来就是在问你“你家的板子是哪一个?”
其实原因不复杂。RK3568 是一颗通用 SoC,瑞芯微官方为它设计了多个评估板版本,比如 EVB1、EVB2、EVB6、EVB7。不同版本用的是不同类型的内存,DDR4、LPDDR4、LPDDR4X 都有,DDR 控制器初始化和电压时序配置可能不同;板载外设也有差异,比如 MIPI DSI 屏、HDMI、eDP 屏、不同型号的触摸屏和摄像头。SoC 本身是同一颗,但板级电路不同。设备树的作用正是告诉内核“我这块板子长什么样、外设接在哪个引脚上”。所以板子有多版本,设备树自然就多。
再加上 OpenHarmony 生态里做 RK3568 开发板的厂商不止一家,润和、瑞芯微官方都有自己的产品配置,每个产品会引用不同的 dts。我在源码里见过一个版本中同一款 SoC 对应十几个 dts 文件的情况,第一次看到确实头大。
3.2 判断板子该用哪个设备树的三条线索
我的经验是不要背文件名,而是按下面三条线索去排查。
第一条,先看内存颗粒。这是 RK3568 选 dts 最关键的维度。打开板子背面看内存芯片丝印,或者查开发板规格书,确认是 DDR4 还是 LPDDR4、LPDDR4X。文件名里带ddr4的就是对应 DDR4 的板型,带lpddr4或lpddr4x对应的就是低功耗内存版本。选错这一层,轻则启动报错,重则内核直接 panic。
第二条,看硬件版本和板型。很多开发板丝印或者包装上会写着 V1.1、V2.0 之类,或者直接标 EVB1、EVB2。源码里的 dts 文件名通常带着板型标识,比如rk3568-evb1-ddr4-v10-linux.dts。如果你的板子明确是 EVB1 + DDR4,那这个文件基本就是对的。
第三条,看外设接口。如果板子接了 MIPI DSI 屏、HDMI 显示、特定型号的摄像头和触摸屏,尽量找对应 dts 里是否包含这些外设节点。厂商一般会在 dts 里通过注释说明当前配置的屏幕和摄像头型号。我实际开发中遇到过一种情况:核心板没问题,但 dts 里使能的屏幕不对,结果编译烧录都成功,屏幕上就是没画面。
部分 OpenHarmony 版本在编译时会在产品配置里直接指定 dts 名称,具体位置可能在vendor/企业名/产品名/config.json或内核构建脚本里。如果你用的是润和 DAYU200:它基于 RK3568 EVB 板型,常用的是 DDR4 版本的 dts。但每个发行版路径和命名会有差异,拿到源码后先搜索rk3568.dts或rk3568-evb关键字,找到实际编译引用的那份。
3.3 选错设备树的真实表现与补救方法
很多人以为选错 dts 不会怎样,大不了不能显示。实际远不止如此。我总结了几类典型症状:
| 现象 | 大概率原因 |
|---|---|
| 内核启动阶段卡住,串口日志停在 DDR 初始化或早期类似 hang 的状态 | 内存类型不匹配 |
| 内核能起来,但屏幕黑屏或者无信号 | 显示接口或屏幕型号配置错误 |
| 触摸屏没反应,但系统能正常进入桌面 | 触摸 I2C 地址、中断 GPIO 配置不对 |
| 摄像头打不开,报媒体相关错误 | Sensor 型号或 MIPI CSI 配置不匹配 |
| 频繁重启,或者部分外设工作不稳定 | 电源、GPIO 复用等板级配置有问题 |
补救的方法不复杂,改 dts 文件里对应的节点,重新编译内核和整包,再烧录验证。但如果你刚开始接触,建议优先找厂商或者社区里和自己板子一致的开箱方案,不要自己从头配 dts,那是一条极其“肝”的路。RK3568 在 Linux 内核生态里的资料非常丰富,遇到外设不工作,可以先查 Linux SDK 里同一块板子的 dts 是怎么配的,再照着移植到 OpenHarmony 内核侧,效率会高很多。
4. 在 x86 电脑上跑 OpenHarmony:能做,但别被带偏
4.1 x86 版 OpenHarmony 的真实形态
搜索“电脑版 x86 OpenHarmony”时,你会发现很多教程。这里先给一个明确结论:OpenHarmony 确实支持 x86_64 架构,而且官方源码里早就提供了 qemu-x86_64 和 x86_64 相关产品定义,主要用途是跑模拟器,方便应用开发者不用买实体开发板也能调试应用。但你看到的所谓“PC 版”,很多是基于官方模拟器镜像或者开发者自行移植之后做成系统引导出来的实验性版本,不是像普通 Linux 发行版那样为桌面 PC 完整适配的系统。
在源码树里,x86_64 相关的线索主要分布在productdefine/products、device/qemu、vendor这些位置。如果你用hb set查看可选产品,里面一般能看到 qemu-x86_64 或类似名字。选它编译,输出的是面向 QEMU 模拟器的镜像。把它通过 GRUB 引导物理机启动,理论上可行,但显卡、声卡、Wi-Fi、触摸这些硬件的驱动就非常看脸了。
4.2 x86 移植时源码树里的关键改动
如果真的想在物理机上跑 x86 版 OpenHarmony,源码树里需要动的主要有三块。
第一块是内核配置。OpenHarmony 标准系统默认侧重 ARM 平台,x86 内核需要打开 x86_64 架构相关选项,还要确认文件系统、帧缓冲、virtio 驱动是否开启。物理机跑还要额外配置显卡和网卡驱动,这部分通常并不轻松。
第二块是产品配置。你需要在productdefine/products下找到 x86_64 的产品定义,或者基于官方 qemu-x86_64 产品扩展出新的产品配置。这里会涉及启用了哪些系统组件、包管理策略、默认应用列表等,改起来要小心,组件裁剪过头系统可能无法启动。
第三块是根文件系统与引导方式。x86 一般走 GRUB 引导,内核和 ramdisk 的加载路径、根文件系统挂载参数都要按实际分区去改。我见过很多 x86 移植启动失败,都是引导参数里传给内核的 root 参数写错,导致内核起完后找不到根文件系统。
还有一个容易踩的坑:x86 版本里很多 ARM 相关的 HDF 驱动配置会被裁剪,外设框架和 ARM 板卡上的表现差异很大。社区里一些移植教程会把“能进桌面”当成“跑通了”,但实际触摸、网络、GPU 加速都还是残缺的。如果你想在电脑上体验一下 OpenHarmony 的交互,用官方 QEMU 镜像或者基于开发板的模拟器是更省心的路径;如果你想深入移植和内核适配,那请做好长时间调试的准备。
4.3 运行起来之后的边界与限制
即便你成功在 x86 平台上把 OpenHarmony 跑起来了,也要清楚它的定位。OpenHarmony 的目标场景是物联网、智能终端、带屏设备,和通用桌面系统不完全是一回事。在物理机上它能流畅运行的软件,都是围绕 OpenHarmony 应用生态开发的,不是直接跑 Linux 的.deb或 Windows 的.exe。你没看错,生态边界是最大的限制。
我在做了几次 x86 相关尝试后,反而更推荐大多数人用 x86 模拟器来做应用层开发。源码树里已经帮你搭好了模拟器的基础设施,省去硬件驱动适配,启动速度快,调试也方便。真要玩硬件适配,还是老老实实拿一块 RK3568 开发板更实在。
5. 二开从改源码树开始:三个最常动的位置
5.1 改开机界面与产品名称
源码已经能编译烧录之后,很多人想做的第一件事就是“把系统改成自己的”。最常见的需求是改开机 logo 和产品名称。
开机 logo 一般在设备适配目录下。以 RK3568 为例,你可以在device/board/rockchip/rk3568/kernel/logo这类目录里找到logo.bmp、logo_kernel.bmp等文件。替换这些位图文件,重新编译内核或者整包,开机画面就会变成你的图片。有一点要注意:图片格式、分辨率、色深最好和原图保持一致,否则内核 logo 显示时可能花屏或者自适应缩放出问题。
产品名称一般在productdefine/products下对应的产品 json 文件里。里面会有类似product_name、product_manufacturer这样的字段。改完重新编译,系统设置里看到的产品信息就会跟着变。这个操作看似简单,却是很多定制项目的起步点,改好之后整个源码树就有了“专属”的味道。
5.2 修改默认配置与版本号
版本号也是高频修改点。OpenHarmony 源码树里通常有一个版本相关的配置文件,或者位于build/version.cfg,或者位于产品目录下的配置中。搜索const.product.version之类的关键字,大概率能找到版本定义的位置。改版本号时建议把大版本、小版本、构建号一起规划好,别直接拿来就改,否则后面做版本管理和问题溯源会很痛苦。
除了版本号,系统默认时区、默认语言、默认 WiFi 配置这些也都是在源码或产品配置里控制的。你要做行业定制设备时,这些默认值往往比单纯改个名字更重要。我建议每改一处都做好记录,并在编译前对改动文件做一次 diff 检查,因为 OpenHarmony 的配置继承关系比较多,一个小小的缩进或者引号问题,就可能让整个构建失败。
5.3 重新编译与烧录验证
源码改动后重新编译的流程并不复杂,核心就是hb set选择目标产品,再hb build -f全量或者增量编译。但如果改了内核、dts、产品配置,我的建议是先做一次全量编译,避免因为旧的中间产物导致问题“假性复现”。全量编译很吃时间和机器性能,我试过在性能一般的笔记本上编译 RK3568 版本,跑一次大几个小时很正常,所以编译前一定要确认好改动。
烧录时 RK3568 一般用瑞芯微提供的烧录工具,把编译产物中的boot.img、system.img、vendor.img等镜像按分区地址烧进去。如果一个镜像选错,系统可能就卡在开机阶段。烧录和编译一样,都要养成“先备份原厂镜像、再刷自己镜像”的习惯,排错时才有的对比。
6. 源码树实操中的常见坑与排查技巧
6.1 编译环境与工具链问题
源码树本身再干净,环境不对也编译不过。OpenHarmony 对宿主机的 Python、Node.js、hb 工具链版本都有要求。我见过太多人在编译第一步就翻车:hb命令找不到,或者hb version和源码版本不匹配,又或者 Node 版本过高导致构建脚本执行失败。
这些问题没有太多捷径,先读 README,再按官方文档装对应版本的工具。hb找不到时,通常是环境变量没有配置好,可以尝试在源码根目录执行source build/envsetup.sh后再用。另外,编译过程切记不要随便中断,OpenHarmony 构建系统对中间态的处理不算很稳定,中断后残留的临时文件很容易引发奇怪的编译错误,最坏情况就是清掉 out 目录重新来。
6.2 仓库同步与版本匹配问题
用 repo 同步源码树时,容易遇到同步中断或者个别仓库拉取失败。通常的做法是稍微下调并发数,或者先同步到一部分再继续。同步完成后一定要检查关键目录是否存在,比如foundation、device、vendor这些目录不完整,编译时会出现大量找不到模块的错误。
还有一类坑是分支和 tag 不匹配。manifest 里记录的是某个版本的精确提交,如果你中途手动切过分支,或者混用了不同版本的源码目录,编译时会表现出一堆匪夷所思的错误:头文件找不到、接口对不上、构建脚本版本不兼容。遇到这类情况,先检查源码树当前分支和 manifest 预期是否一致,再考虑重拉或切分支。养成“一个版本用一个干净的源码目录”的习惯,能省掉不少时间。
6.3 排错心法:从启动日志倒推源码位置
设备起不来、外设不工作这类问题,我强烈建议不要上来就翻代码,而是先从串口日志和内核日志入手。RK3568 开发板一般都会引出串口调试口,接上 USB 转串口工具,波特率通常是 1500000,在系统启动时就能看到 bootloader 和内核的输出。日志里往往会直接告诉你哪一步出错,比如找不到根文件系统、DTS 里某个设备 probe 失败。
拿到日志后,再去源码树里搜索对应的设备名、驱动名或者错误关键字。比如日志里说某个 I2C 设备 probe 失败,你就去 dts 里查这个 I2C 节点是否使能,去内核源码里找对应驱动,看支持哪些型号、匹配规则是什么。这套“日志到源码”的排查流程我一直在用,效率远高于漫无目的地翻代码。
另外要提醒的是,串口工具的地线和 TX、RX 不要接反,我因为这个踩过好几次坑。接反的表现是串口完全没有数据输出,容易误判为硬件损坏或者系统没启动,其实是连接方式的问题。推荐第一次用之前,先短接串口工具的 TX 和 RX 自发自收,确认工具本身正常工作再接板子。
我个人在实际操作中的体会是,源码树学习最忌讳的是贪多求全。不要试图把所有目录都读一遍,而是先定目标:你是想做应用,就深耕 foundation/ability 和 ark;你是想做系统定制,就把 vendor、productdefine 和 base/startup 吃透;你是想搞硬件适配,那 RK3568 的设备树至少要看半个月。把一条线走通,比看十遍目录结构有用得多。还有个小技巧,修改任何源码前先搜索这个文件是否被其他模块引用,用 hb 编译时留意相关依赖,很多编译错误其实都是改了一处、连累了三处造成的。
OpenHarmony 的源码树是一座宝库,也是一个迷宫。希望这篇解剖能帮你少走几步弯路,至少在打开目录时,能清楚地知道自己正站在地图的哪个位置。