OpenHarmony 技术拆解(一):多内核兼容与硬件能力发布机制

OpenHarmony 技术拆解(一):多内核兼容与硬件能力发布机制

做嵌入式系统移植时,最麻烦的往往不是业务代码,而是底层差异。

同样是创建线程,Linux 用 pthread_create(),LiteOS 用 LOS_TaskCreate();同样是等待信号量,不同内核的 API、超时语义、返回值都不一样。文件系统、内存映射、中断处理、定时器也都有类似问题。

OpenHarmony 面对的问题更复杂。它需要覆盖标准系统、小型系统和轻量系统,对应的底层内核分别可能是 Linux、LiteOS-A 和 LiteOS-M。也就是说,同一套系统框架既要能跑在手机、平板、车机这类资源充足的设备上,也要能跑在 MCU 或轻量 IoT 节点上。

这篇文章围绕一个核心问题展开:OpenHarmony 如何在不同内核之上建立统一的系统服务、驱动框架和硬件能力发布机制。

文章重点讨论四件事:

  1. OpenHarmony 为什么需要同时支持 Linux、LiteOS-A、LiteOS-M。

  2. OSAL 和 HDF 分别解决什么问题,它们不解决什么问题。

  3. Linux 原生设备驱动是否需要迁移到 HDF,以及如何发布成 OpenHarmony 硬件能力。

  4. 被发布的硬件能力如何被其他节点使用,UART、Timer、IRQ 这类启动关键能力又该如何处理。


1. 多内核兼容的整体分层

OpenHarmony 的多内核兼容不是在一个内核里做很多开关,而是在系统架构上把"内核相关"和"内核无关"的部分隔离开。

从上到下可以分成几层:

  • 应用层:ArkTS、JS、C/C++ 应用。

  • 框架层:Ability、ArkUI、多媒体、通信、分布式能力等。

  • 系统服务层:包管理、窗口、电源、传感器、输入、音频、显示等服务。

  • HDF 驱动框架:统一驱动模型、设备管理、Host、Dispatch。

  • OSAL 操作系统抽象层:线程、锁、信号量、定时器、内存、文件、I/O 等基础 OS 能力封装。

  • 内核层:Linux、LiteOS-A、LiteOS-M。

  • 硬件层:SoC、MCU、传感器、通信模组、显示屏等。

!外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Finternal-api-drive-![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/4106ff221c0a463a9c5389819eec6196.png)

图1展示了这套分层关系。

图1的关键点在于,系统服务及以上不直接感知底层内核。它们通过 HDF/HDI 访问硬件能力,而 HDF 内部需要调用线程、锁、内存、I/O 等基础能力时,再通过 OSAL 适配到底层内核。

因此,OpenHarmony 的多内核兼容并不是靠上层代码到处写 #ifdef Linux#ifdef LiteOS,而是把差异集中收敛到 HDF 适配层和 OSAL 实现里。


2. 三类内核对应三种系统形态

OpenHarmony 支持三种典型系统形态:标准系统、小型系统和轻量系统。它们面对的硬件规模差异很大。

内核 系统形态 典型设备 MMU 内存规模
Linux 标准系统 手机、平板、车机、开发板 通常大于 128 MB
LiteOS-A 小型系统 智能屏、摄像头、部分工业设备 通常大于 1 MB
LiteOS-M 轻量系统 MCU、传感器节点、低功耗 IoT 设备 可低至几十 KB

Linux 提供完整的进程模型、VFS、网络协议栈、驱动模型和内存管理,适合标准系统。LiteOS-A 更轻量,但仍有 MMU 和类 POSIX 能力,适合小型系统。LiteOS-M 面向 MCU,通常没有 MMU,任务共享地址空间,很多能力直接依赖 BSP/HAL。

硬件规模差异决定了 OpenHarmony 不可能用一个内核覆盖所有场景。它的策略是:

  • 上层服务和接口尽量统一。

  • 底层内核根据设备形态选择。

  • 内核差异通过 OSAL、HDF 适配层和产品配置隔离。


3. OSAL 解决的是基础 OS API 差异

OSAL,全称 Operating System Abstraction Layer,作用是为上层组件提供统一的基础操作系统接口。

这些接口通常包括:

  • 线程创建与销毁。

  • 互斥锁、信号量、自旋锁。

  • 定时器。

  • 内存分配和映射。

  • 文件操作。

  • I/O 操作。

  • 中断注册。

  • 时间获取。

上层代码不直接调用 pthread_create()LOS_TaskCreate() 或 CMSIS RTOS API,而是通过 OsalThreadCreate() 这一类接口间接访问底层能力。

图2展示了 OSAL 的映射方式。

以线程、锁、信号量为例,不同内核的映射大致如下:

C 复制代码
// Linux
OsalThreadCreate() -> pthread_create() / kthread_run()
OsalMutexLock()    -> pthread_mutex_lock() / mutex_lock()
OsalSemWait()      -> sem_wait()
C 复制代码
// LiteOS-A
OsalThreadCreate() -> LOS_TaskCreate()
OsalMutexLock()    -> LOS_MuxPend()
OsalSemWait()      -> LOS_SemPend()
C 复制代码
// LiteOS-M
OsalThreadCreate() -> LOS_TaskCreate() / CMSIS osThreadNew()
OsalMutexLock()    -> LOS_MuxPend() / CMSIS osMutexAcquire()
OsalSemWait()      -> LOS_SemPend() / CMSIS osSemaphoreAcquire()

OSAL 的价值不在于把 Linux API 再包一层。对于 Linux 来说,OSAL 很多时候只是薄封装。它真正的价值在于:同一套 HDF 驱动框架或可移植组件,在切到 LiteOS-A、LiteOS-M 时不需要重写基础 OS 调用。

不过,OSAL 只能统一接口,不能完全消除语义差异。

例如:

  • Linux 和 LiteOS-A 有 MMU,mmap 可以表示虚拟地址映射;LiteOS-M 通常没有 MMU,类似接口可能只是在物理地址或静态内存上做封装。

  • Linux 的中断上下文、进程上下文分得很清楚;LiteOS-M 的模型更接近传统 MCU RTOS,任务和中断的边界不同。

  • Linux 的线程调度、优先级、阻塞语义与 LiteOS 并不完全一致。

因此,使用 OSAL 并不意味着不用理解底层内核。它解决的是工程上的接口收敛,不是把所有内核语义变成同一种东西。


4. OSAL 不参与 Linux 启动

讨论 OpenHarmony 标准系统时,一个常见误区是把 OSAL 理解成 Linux 启动的一部分。

实际上,Linux 的启动链路完全由 BootROM、Bootloader 和 Linux Kernel 自己完成。OSAL 在这个阶段并不存在。

标准系统的启动过程可以简化为:

Plain 复制代码
BootROM -> SPL -> U-Boot -> Linux start_kernel() -> rootfs -> OpenHarmony init

在 Linux start_kernel() 阶段,内核会完成 MMU、调度器、中断控制器、时钟、驱动核心、VFS、根文件系统等初始化工作。这些都属于 Linux 内核职责,不依赖 OpenHarmony OSAL。

OpenHarmony 真正接管系统,是在 Linux 内核拉起第一个用户态进程之后。标准系统中,这个进程是 OpenHarmony 的 init。

OH init 读取启动配置,按阶段拉起系统服务,例如:

  • samgr

  • foundation

  • hdf_devmgr

  • hdf_host

  • 各类 native service

OSAL 从这些系统服务和 HDF 组件运行后才开始发挥作用。图3展示了 Linux 启动链路中 OSAL 的位置。

所以,更准确的说法是:OSAL 不支持 Linux 启动,它支持 OpenHarmony 在 Linux 启动完成之后继续以跨内核方式运行系统服务和驱动框架。


5. HDF 统一的是驱动模型和硬件服务接口

OSAL 解决基础 OS API 差异,HDF 解决驱动模型和硬件服务接口的统一。

HDF 大致可以分为几层:

  1. 驱动管理层:负责驱动加载、设备管理、Host 管理、Dispatch。

  2. 驱动模型层:例如 Platform、Sensor、Display、Input、Audio、WLAN、USB 等模型。

  3. 适配层:在需要调用底层 OS 或 Linux 原生接口时进行适配。

图3展示了 HDF 与不同内核之间的关系。

在标准系统上,HDF 并不是用来替代 Linux Driver Model 的。Linux 原生驱动体系仍然存在,HDF 更像是 OpenHarmony 的硬件服务治理层。

两条访问路径可以并行存在:

Plain 复制代码
OpenHarmony 应用 / 系统服务
        |
        |-- HDI / HDF -> HDF Service / HDF Host -> Linux 原生接口或 HDF 驱动
        |
        |-- syscall / ioctl / read / write -> Linux 原生驱动

这意味着,Linux 标准系统上的设备驱动不需要全部迁移到 HDF。是否迁移取决于这个硬件能力是否需要被 OpenHarmony 统一管理,是否需要通过 HDI 暴露给上层,是否需要进一步通过 DSoftBus 发布给其他节点。


6. Linux 原生驱动是否需要迁移到 HDF

标准系统中,Linux 内核已经提供了完整驱动体系。例如:

  • 块设备驱动。

  • 网络驱动。

  • V4L2 Camera 驱动。

  • ALSA Audio 驱动。

  • IIO 传感器驱动。

  • GPIO、PWM、I2C、SPI 等基础总线和控制器驱动。

这些驱动没有必要全部重写成 HDF。更合理的做法是分类处理。

6.1 必须纳入 HDF/HDI 的能力

如果硬件能力需要被 OpenHarmony 系统服务统一管理,或者需要通过分布式能力暴露给其他设备,一般需要纳入 HDF/HDI。

典型场景包括:

  • 分布式传感器。

  • 分布式显示。

  • 输入设备统一管理。

  • USB 设备管理。

  • 需要被上层框架标准化调用的音频、显示、相机、传感器等模块。

这类能力的重点不是"驱动必须重写",而是必须提供标准 HDI 接口。

6.2 封装适配即可的能力

很多外设底层仍然使用 Linux 原生驱动,上层通过 HDF/HDI 或 HAL 做一层封装。

例如:

  • Camera 底层仍可能走 V4L2 / Media Controller。

  • Audio 底层仍可能走 ALSA / tinyalsa。

  • WLAN 底层仍可能走 cfg80211 / nl80211。

  • Sensor 底层可能走 IIO、input 子系统或厂商私有节点。

这种方式更接近 Adapter 模式:底层驱动不动,上层提供 OpenHarmony 统一接口。

6.3 可以保留 Linux 原生访问的能力

如果某个硬件只在本机使用,不需要通过 OpenHarmony 系统服务治理,也不需要分布式发布,可以继续使用 Linux 原生接口。

例如:

  • eMMC、SD、NAND 等块设备。

  • 内部 I2C/SPI 总线上的 PMIC 或板级控制器。

  • 只通过 sysfs 控制的简单 GPIO/PWM。

  • 看门狗、部分电源管理、内部调试设备。

图4给出了一个判断流程。

工程上建议先保留 Linux 原生驱动,把内核、rootfs、OH init 和基础服务跑通。只有当上层服务需要 HDI 接口,或者硬件能力需要分布式发布时,再做 HDF/HDI 适配。


7. 如何把 Linux 原生驱动发布成 OpenHarmony 硬件能力

把 Linux 原生驱动接入 OpenHarmony,不等于重写内核驱动。常见做法是在原生驱动之上增加 Adapter 和 HDI Server。

以一个 Linux IIO 传感器为例,底层已经存在这些节点:

Plain 复制代码
/dev/iio:device0
/sys/bus/iio/devices/iio:device0/in_accel_x_raw
/sys/bus/iio/devices/iio:device0/in_accel_y_raw
/sys/bus/iio/devices/iio:device0/in_accel_z_raw

推荐的发布路径如下:

Plain 复制代码
Linux 原生驱动
    |
    |-- /dev / sysfs / ioctl / read / mmap
    |
Adapter 适配层
    |
    |-- 将 Linux 私有接口翻译成模块语义
    |
HDI Server
    |
    |-- 实现 IDL 生成的接口
    |
HDF/HCS 注册
    |
OpenHarmony 系统服务
    |
DSoftBus 远端代理

图5展示了这个路径。

Adapter 层可以直接使用 Linux 原生接口,例如:

C 复制代码
int32_t LinuxAccelAdapterEnable(int32_t sensorId)
{
    int fd = open("/dev/iio:device0", O_RDONLY);
    if (fd < 0) {
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

int32_t LinuxAccelAdapterRead(struct SensorData *data)
{
    /* 从 sysfs、ioctl 或 mmap ring buffer 中读取数据,
       再转换成 OpenHarmony SensorData 结构。 */
    return HDF_SUCCESS;
}

随后通过 IDL 定义 HDI 接口:

Plain 复制代码
package ohos.hdi.sensor.v1_0;

interface ISensorInterface {
    EnableSensor([in] int sensorId);
    DisableSensor([in] int sensorId);
    SetBatch([in] int sensorId, [in] long samplingInterval);
    GetAllSensorInfo([out] struct SensorInfo[] info);
}

IDL 编译后生成 client/server 相关代码。服务端实现中再调用 Adapter:

C 复制代码
int32_t EnableSensor(int32_t sensorId)
{
    return LinuxAccelAdapterEnable(sensorId);
}

然后通过 HCS/HDF 注册服务:

C 复制代码
sensor_host :: host {
    hostName = "sensor_host";
    priority = 100;
    sensor_device :: device {
        device0 :: deviceNode {
            policy = 2;
            priority = 100;
            moduleName = "sensor_driver";
            serviceName = "sensor_service";
            match_attr = "linux_accel_sensor";
        }
    }
}

这样,上层看到的是标准 HDI 能力,底层仍然可以保留成熟的 Linux 驱动。


8. 被发布的硬件能力如何被其他节点使用

OpenHarmony 的分布式能力不是把一个设备的驱动搬到另一个设备上运行。远端节点拿到的是代理对象,真正访问硬件的仍然是硬件所在节点。

假设节点 A 上有一个传感器,已经通过 HDI 发布;节点 B 希望使用这个传感器。调用链路可以简化为:

Plain 复制代码
节点 B 应用
    |
Remote Proxy
    |
DSoftBus 会话通道
    |
节点 A HDI Service
    |
Adapter / HDF Driver
    |
Linux 原生驱动或 LiteOS 驱动
    |
真实硬件

远端调用过程通常包括:

  1. 设备发现:DSoftBus 发现附近可信设备。

  2. 认证组网:完成设备认证、可信关系校验和组网。

  3. 能力发现:系统服务识别远端节点提供的硬件能力。

  4. 获取代理:调用方拿到 Remote Proxy。

  5. 发起调用:Proxy 将请求序列化,经 DSoftBus 发送到硬件所在节点。

  6. 本地执行:节点 A 的 HDI Service 调用本地 Adapter 或 HDF Driver。

  7. 数据返回:结果或数据流经 DSoftBus 回传给节点 B。

控制类接口通常走同步调用,例如 Enable、Disable、Config。数据上报更适合异步回调或流式传输,例如传感器连续采样、音视频数据等。

DSoftBus 提供多类传输通道:

通道类型 适合场景 示例
message 控制命令、小数据 开关传感器、配置参数
bytes 中等大小结构化数据 传感器批量数据、定位坐标
stream 连续流数据 音频、视频、高频传感器数据
file 文件传输 日志、图片、升级包

远端节点不访问 /dev/iio:device0,也不加载节点 A 的 .ko。它调用的是标准化能力代理。


9. 被发布的驱动能力是否必须使用 OSAL 编写

不一定。

OSAL 适用于需要跨内核复用的代码。如果一段 HDF 驱动逻辑需要同时跑在 Linux、LiteOS-A 和 LiteOS-M 上,就应该使用 OSAL 来封装线程、锁、内存、中断、定时器等基础能力。

但如果只是标准系统上的 Linux-only Adapter,直接使用 POSIX、syscall、ioctl()read()write() 通常更直接。

可以按下面的原则判断:

场景 是否建议使用 OSAL
跨 Linux、LiteOS-A、LiteOS-M 的 HDF 通用驱动逻辑 建议使用
只在 Linux 标准系统运行的 HDI Adapter 不强制,直接使用 Linux 原生接口更清晰
用户态服务封装 /dev、sysfs、V4L2、ALSA、cfg80211 通常不需要
启动早期 BSP、Bootloader、内核自举代码 不能依赖 OSAL

OSAL 是跨内核移植工具,不是所有硬件能力发布都必须经过的唯一入口。


10. UART、Timer、IRQ 的处理边界

UART、Timer、IRQ 这类能力比较特殊,因为它们既可能是操作系统启动基础设施,也可能是业务外设能力。

必须先区分两个阶段:

阶段 使用者 目的 处理方式
启动支撑阶段 Bootloader、内核、BSP 让系统先启动起来 OS/BSP 原生处理
业务运行阶段 应用、系统服务、HDF/HDI 对外提供外设访问能力 可通过 Linux 原生接口、OSAL、HDF/HDI 封装

10.1 UART

启动阶段的串口一般用于 early console、Bootloader 日志、内核启动日志。这条链路通常是:

Plain 复制代码
BootROM -> SPL -> U-Boot -> Linux earlycon / console

这些能力在 HDF 启动之前就必须可用,因此不能依赖 HDF 或 OSAL。

系统运行后,如果某个业务模块需要访问串口,例如 GPS 模块、4G 模块 AT 命令,可以有两种方式:

  • 直接操作 /dev/ttySx,使用标准 Linux 用户态接口。

  • 通过 HDF UART 模型或 HDI 服务封装成标准能力。

本机使用时,直接访问 Linux 串口节点通常更简单。需要跨内核复用或远端发布时,再考虑 HDF/HDI。

10.2 Timer

系统调度依赖的 timer 属于内核基础设施。例如 Linux 的 clocksource、clockevent、hrtimer,LiteOS 的系统 tick 和软件定时器基础实现。

这些能力位于 OSAL 之下,不能由 OSAL 负责初始化。

OSAL 提供的 timer 更适合业务逻辑,例如:

Plain 复制代码
Linux     : OsalTimerCreate() -> timer_create() / timer_settime()
LiteOS-A  : OsalTimerCreate() -> LOS_SwtmrCreate()
LiteOS-M  : OsalTimerCreate() -> LOS_SwtmrCreate() / CMSIS osTimerNew()

10.3 IRQ

中断控制器初始化由内核或 BSP 完成。例如 Linux 的 irqchip、GIC、device tree 中断描述,LiteOS-M 的 NVIC 配置。

OSAL 不负责初始化中断控制器。它能做的是在驱动运行阶段注册设备中断处理函数,例如:

Plain 复制代码
Linux     : OsalRequestIrq() -> request_irq()
LiteOS-A  : OsalRequestIrq() -> LOS_HwiCreate()
LiteOS-M  : OsalRequestIrq() -> HAL/NVIC 相关封装

因此可以这样理解:

  • 中断控制器归 OS/BSP。

  • 设备中断 handler 归驱动。

  • 如果驱动需要跨内核复用,中断注册可以通过 OSAL 封装。


11. 移植时的工程顺序

如果要把一块 Linux 板子适配到 OpenHarmony 标准系统,不建议一开始就重写驱动。更稳妥的顺序是:

  1. 保留 Linux 原生驱动,先保证内核启动、串口日志、rootfs、基础设备节点正常。

  2. 拉起 OpenHarmony init,确认系统服务启动顺序和权限配置正确。

  3. 验证 /dev/sysdmesghilog,确认底层硬件链路稳定。

  4. 找出需要接入 OpenHarmony 系统服务的硬件能力。

  5. 对这些能力编写 Adapter 和 HDI Server。

  6. 需要分布式发布时,再接入 DSoftBus 能力发现与远端代理调用。

  7. 只有当代码确实需要跨 Linux、LiteOS-A、LiteOS-M 复用时,再将内部 OS 调用整理为 OSAL。

图6给出了一个简化的移植检查清单。

这个顺序的重点是:先让系统启动,再让硬件可用,最后再做标准化和分布式发布。不要在底层链路还没稳定时急着重构驱动模型。


12. 总结

OpenHarmony 的多内核兼容不是把所有驱动都重写一遍,也不是让所有底层能力都走 OSAL/HDF。

更准确的理解是:

Plain 复制代码
启动关键路径:
BootROM / SPL / U-Boot / Linux Kernel / LiteOS Kernel
        |
OS/BSP 原生能力:UART console / timer tick / irqchip / MMU / scheduler
        |
OpenHarmony init:拉起系统服务、HDF Manager、HDI Server
        |
HDF/HDI:把需要治理和发布的硬件能力标准化
        |
DSoftBus:把标准化后的能力暴露给远端节点
        |
远端应用:拿到 Proxy,像调用本地接口一样调用远端硬件能力

可以用几句话收束:

  • OSAL 不参与 Linux 启动,它在系统服务和 HDF 运行后负责抹平跨内核 API 差异。

  • HDF 不替代 Linux Driver Model,它在标准系统上更像硬件服务治理框架。

  • Linux 原生驱动大多数可以保留,需要发布为 OpenHarmony 能力时,优先通过 Adapter + HDI Server 封装。

  • 远端节点使用的是能力代理,不是远程加载驱动。

  • UART、Timer、IRQ 等启动关键能力归 OS/BSP,业务阶段才可能通过 OSAL/HDF/HDI 封装。

最终结论是:OpenHarmony 建立的不是一套新的底层驱动体系,而是在已有 OS 和驱动之上,增加了一套可治理、可分布式、可跨内核复用的硬件能力发布机制。

相关推荐
我命由我123455 小时前
Windows 操作系统 - Windows 查看防火墙是否开启、Windows 查看防火墙放行端口
java·运维·开发语言·windows·java-ee·操作系统·运维开发
老王熬夜敲代码7 小时前
CPU缓存的访问机制
操作系统·cpu
茶马古道的搬运工1 天前
Linux-Ubantu-贴士-建立Docker 沙盒(三)
操作系统
茶马古道的搬运工1 天前
Linux-Ubantu-贴士-apt的地盘
操作系统
带娃的IT创业者2 天前
穿越回 1980:解读微软开源的“最早 DOS 源码”与操作系统的原点
microsoft·微软·开源·操作系统·dos·源码解析·计算机历史
Seven972 天前
select、poll、epoll 到底有什么区别?一文讲透 I/O 多路复用
操作系统
磊 子3 天前
硬中断 软中断
后端·操作系统
mifengxing4 天前
操作系统(五)
linux·运维·服务器·操作系统·王道考研
apcipot_rain4 天前
计科八股20260605——软件生命周期、文档、死锁、地址转换、I/O控制方式、堆、无向图、连通图、最小支配集、逆关系、永真式
数据结构·操作系统·软件工程·计算机组成原理·离散数学