《微内核普及:鸿蒙商用微内核的兼容性和性能》解读

介绍

原文地址: https://www.usenix.org/conference/osdi24/presentation/chen-haibo

原文发表于:18th USENIX Symposium on Operating Systems Design and Implementation

原文第一作者为陈海波教授,目前在上海交通大学任教。

TL;DR(总结):

鸿蒙内核解决/改善了微内核在智能手机、智能汽车等新兴应用场景上的兼容性和性能问题。鸿蒙内核兼容Linux API/ABI,可以用鸿蒙内核来运行AOSP或OpenHarmony,实现对现有软件生态和设备驱动的兼容。鸿蒙内核通过多方面的性能优化,在部分场景上的性能可以接近或者超越宏内核(Linux),但依然有改进的空间。

原文分为八个部分,介绍了微内核想要在现代智能设备上运行会面临哪些性能和兼容性方面的问题,鸿蒙内核为了应对这些挑战做了什么工作。

本文会介绍这篇文章的主要内容,部分内容是基于自身理解来写的,不是原文的一比一翻译。

一、微内核的优缺点

1. 微内核的关键特点

• 最小化原则: 内核只保留最核心、最关键的功能,其他功能(如文件系统和设备驱动程序)移动到用户空间的服务中。

• 最小权限原则: 通过能力来实现细粒度的访问控制,确保每个服务只能访问其执行功能所需的内核对象。

• 安全和可靠性: 由于上述设计和架构的特点,微内核通常比传统的宏内核更安全、可靠。

2. 现有微内核的局限性

• 现有的微内核主要针对特定领域,如嵌入式系统和安全关键型系统。这方面的例子包括嵌入高通蜂窝调制解调器芯片中的 L4、汽车和嵌入式系统中的 QNX1,以及智能音箱中的 Zircon(Fuchsia 内核)。至于如何将微内核扩展为通用操作系统内核,用于智能汽车和智能手机等新兴应用场景,这方面的研究还很少。

• 业界有采用混合内核的解决方案,如 Windows NT 和 Apple XNU,它们将核心微内核(如 XNU 中的 Mach)与内核空间中的所有其他服务作为一个整体结合在一起(如 Windows NT 中的 Executive 和 XNU 中的 BSD)。虽然混合内核也将核心内核的功能最小化,但它们并没有继承微内核的许多优点。例如,混合内核中的操作系统服务既不是最小权限,也没有很好地隔离。因此,任何有缺陷或有漏洞的操作系统服务都可能破坏系统,造成严重后果,如损坏用户数据。

鸿蒙内核提出了新的解决方案:将微内核扩展为通用操作系统内核,针对新兴应用场景做针对性的优化,解决微内核在智能手机、智能汽车等设备上运行时的性能和兼容性问题。

二、Linux的局限性

为什么不使用已有的、开源的、社区运营的、成熟的Linux内核,转而要重新开发一个内核?文中提到了一些Linux内核的局限性。

1. 安全性问题:

• 模块化架构导致的安全漏洞: Linux 的内核模块化架构导致文件系统、设备驱动等模块占据了其 30% 的代码。这些模块是 Linux 安全漏洞的主要来源,占据了过去四年 1000个CVE 安全漏洞数量的 90%。

• 模块 API 的不稳定性和安全补丁: 内核模块 API 的不稳定性和安全补丁的频繁更新,迫使系统需要频繁地升级。这给实际部署带来了很大的挑战,导致很多产品仍在运行过时的 Linux 版本(例如国内流行的CentOS 7内置的Linux内核版本是3.10,而最新的Linux内核版本已经是6开头了),从而暴露在安全漏洞之下。

2. 通用性与专业性的矛盾:

• 通用性带来的性能问题: Linux 为了满足通用场景的需求,进行了很多优化,但这些优化主要集中在服务器和云领域。这使得 Linux 在其他场景——如在智能汽车和智能手机上运行——的性能受到影响。

• 专业化的需求与定制化的困难: 随着设备硬件的多样性和场景的差异化,需要针对特定场景进行优化。但由于内核模块之间的紧密耦合,对内核进行定制化需要付出巨大的工程代价,这使得 Linux 很难满足不同场景的专业化需求。

3. 定制化与演进的矛盾:

• 与上游同步的困难: 如果定制过的内核代码需要与上游进行同步,内核 API 的频繁变化会导致同步工作量大,且容易引入性能问题。这使得定制过的内核代码难以在真实环境中部署。

• 过时的内核版本: 由于同步和升级的困难,市场上许多产品仍在运行过时的 Linux 版本,这些版本存在已知的安全漏洞,且无法获得新的安全补丁。

鸿蒙内核做了什么?

通过下面的表格,我们可以一览鸿蒙内核与其它微内核或者混合内核在设计上的区别。

下图是鸿蒙内核分别部署在智能手机和路由器时的架构。其中(a)代表智能手机,(b)代表路由器。不同颜色表示不同的隔离等级。把存在耦合的系统服务(图中的①)合并在一起。地址令牌实现了内核对象(图中的②)的协同管理。兼容 ABI 的垫片 (图中的③)实现了二进制兼容。驱动程序容器通过数据面和控制面的分离(图中的⑤),高效地重用 Linux 驱动程序(图中的④)。

原文介绍了鸿蒙内核的三大设计原则:

1. 保持最小化原则:

• 鸿蒙内核的核心只包含必要的功能,例如线程调度器、串行/定时器驱动程序和访问控制,并将所有其他功能作为隔离的 OS 服务放在核心之外。核心使用C语言实现,大概有9W行代码;解耦的OS服务已有超过一百万行代码。

• 鸿蒙内核采用细粒度的访问控制,以保持最小权限原则,从而继承微内核的安全性、可靠性和可扩展性优势。

2. 优先考虑性能:

• 鸿蒙内核不追求过于严格的隔离,而是提供结构支持,通过灵活的组合方式来满足性能和安全性需求。

• 鸿蒙内核采用 RPC 类型的快速路径来调用 OS 服务,并针对资源分配/耗尽/交换等问题进行优化。

• 鸿蒙内核采用差异化的隔离类来降低 IPC 负载,并允许紧密耦合的 OS 服务进行合并,以减少 IPC 频率和内存复制。

• 鸿蒙内核通过地址令牌来实现高效的内核对象协同管理,这为匿名内存的无策略内核分页提供了便利。

3. 最大程度地实现生态兼容性:

• 鸿蒙内核通过一个垫片来实现 Linux ABI 兼容性,该垫片将所有 Linux 系统调用重定向到合适的 OS 服务,并作为中央存储库来存储和转换 Linux 抽象(例如fd),从而高效地支持如poll 等函数。

• 鸿蒙内核通过驱动容器高效地重用未经修改的 Linux 设备驱动程序,该驱动容器提供了一个来自 Linux 主线的、经过小幅修改的 Linux 运行时。该容器还通过控制面和数据面的分离来进一步提高驱动程序的性能。

三、鸿蒙内核的性能优化

1. 进程间通信机制

在微内核中,频繁的IPC操作可能成为性能瓶颈。为了应对这一挑战,鸿蒙内核设计了一种类似同步RPC的IPC快速路径机制,旨在减少进程间通信的延迟和开销。在实验结果中(实验在Raspberry Pi 4b上进行),这种通信机制可以在多个典型应用场景中显著提高系统的整体性能。

2. 隔离等级机制

如果对所有的服务一视同仁,采取相同的隔离机制,会导致额外的性能开销。鸿蒙内核根据服务的性能要求、安全性、以及是否包含第三方代码等因素,将其划分为不同的隔离类别。通过不同的隔离级别,可以降低 IPC 负载,提高性能,同时保证安全性。

鸿蒙内核目前有以下几个隔离等级:

• 隔离类别 0(IC0):核心 TCB (Core TCB)

仅用于经过严格验证、性能至关重要的可信服务,例如 ABI 兼容的垫片。

与内核没有隔离,IPC 是间接函数调用。

• 隔离类别 1(IC1):强化机制的隔离 (Mechanism-enforced Isolation)

用于性能要求高且经过验证的服务。

服务位于内核空间,使用特定机制(例如 ARM watchpoint 和 Intel PKS)防止跨域内存访问。

IPC 需要通过内核空间中的“门”进行,并进行上下文切换,但无需切换地址空间和调度方式。

• 隔离类别 2(IC2):地址空间隔离 (Address Space Isolation)

用于性能要求不高或包含第三方代码的服务,例如 Linux 驱动。

使用地址空间和权限隔离。

IPC 延迟略高于 seL4,主要是由于细粒度锁机制。

3. 灵活的系统服务组合

并非所有服务都应保持完全解耦,例如文件系统 (FS) 和内存管理器 (MM)。

由于某些功能需要特定服务之间的紧密合作,例如使用内存映射访问文件,因此需要提供结构支持来满足性能需求。

鸿蒙内核可以根据场景的需求,将紧密耦合的服务进行合并,从而降低 IPC 频率和延迟,并降低内存占用,但仍然保留在特定场景中分离它们的能力。

4. 基于地址令牌的访问控制

传统内核使用能力 (Capability) 来控制对内核对象的访问,但这会导致以下问题:

• 访问速度慢:每次访问内核对象都需要将能力属性传递给内核,导致额外的性能开销。

• 效率低:内核对象的内容经常需要由内核之外的 OS 服务更新,导致额外的性能开销。

• 安全性受限:能力只能描述内核对象的授权链,无法限制对内核对象内容的具体操作。

鸿蒙内核提供了一个名为“地址令牌”的机制,将内核对象的物理页面映射到 OS 服务的地址空间,该映射地址作为访问内核对象的令牌。该机制提供了两种访问方式:只读和读写,这两种访问方式都不需要内核参与。只有包含特定内核结构体中特定变量值的内核对象才能进行写操作,并会在读写内核对象时进行额外验证,避免对内核对象的非法访问。在性能上,OS 服务和内核可以异步交换消息,也可以直接更新内核可能同时读取的对象,显著提升了性能。

应用场景:

• OS 内部管理:OS 服务可以通过地址令牌直接访问和更新内核对象,例如页表、操作日志、虚拟内存空间等,提高管理效率。

• 性能关键事件处理:OS 服务可以在内核中直接处理性能关键事件,例如页面错误,且不会违反微内核设计原则。

下图是在树莓派4b上运行不同内核时,访问内核对象的延迟表现。其中“Addr-rd”和“Addr-wr”是鸿蒙内核下基于地址令牌机制的延迟;“ROAddr-wr”是试图对只读对象进行写操作的延迟。

5. 无策略的内核分页机制

鸿蒙内核提供了一种改进的内存管理机制,称为无策略内核分页 (Policy-free Kernel Paging)。这种机制旨在解决传统微内核中匿名内存分页的低效问题,从而提升性能。

传统微内核分页的局限性:

• 用户空间分页: 传统的微内核通常将内存管理委托给应用程序,每个应用程序拥有自己的自定义分页器。这种方法的缺点是缺乏中央管理,难以实现诸如控制组 (cgroup) 和内存回收等需要全局视角的功能。

• 内核空间分页: 一些微内核 (如 seL4) 则将内存管理放在内核空间,由内核中的内存管理器处理。这种方法可以提供中央管理,但分页过程缓慢,因为需要进行额外的内核空间进程间通信 (IPC)。

无策略内核分页的解决方案:

• 预先分配页: 鸿蒙内核预先分配一组物理页面,并将它们映射到内核空间的地址空间。这些页面作为缓存,用于处理匿名内存 (如堆栈和堆) 的页故障。

• 无策略页故障处理: 当发生页故障时,内核首先检查虚拟地址,如果它位于预先分配的页的地址范围内,则直接映射到该页,并记录操作日志 (OPLog)。否则,内核会像传统微内核一样进行 IPC,将请求发送到内存管理器。

• 协同管理: 内存管理器使用地址令牌与内核协同管理页表、操作日志、虚拟空间 (VSpace) 和预分配页缓存 (PCache) 等内核对象。

无策略内核分页的优势:

• 减少 IPC 开销: 通过直接映射预先分配的页,鸿蒙内核消除了内核空间进程间通信 (IPC) 的额外开销,从而显著提高了匿名内存分页的速度。

• 集中管理: 通过集中管理内存,鸿蒙内核可以更有效地实现诸如控制组 (cgroup) 和内存回收等需要全局视角的功能。

• 高效回收: 由于预分配页缓存的存在,鸿蒙内核可以更高效地回收内存,因为它拥有全局视角,可以使用例如最近最少使用 (LRU) 策略进行内存回收。

四、鸿蒙内核的兼容性优化

1. Linux ABI兼容

一个新的系统或者内核,如果没有活跃的生态,那凋亡是迟早的事情。应对这个问题的其中一个手段就是去兼容其它的生态,典型的例子就是Linux上的Wine兼容层,可以在Linux系统上运行Windows软件;还有微软的WSL1,通过对系统调用的虚拟化实现了部分的Linux ABI兼容。

鸿蒙内核兼容了Linux ABI,可以运行AOSP,这在很大程度上缓解了鸿蒙内核在大量设备上的适配困境。HarmonyOS Next有望在大量的旧设备上运行。

Linux ABI 兼容性的挑战:

• 空间的差异: 传统的微内核通常将操作系统服务放在用户空间运行,而 Linux 内核则将所有服务都放在内核空间。这种差异导致实现 Linux ABI 兼容性具有挑战性。

• 系统调用重定向: Linux 应用程序使用系统调用与内核交互,而微内核则需要通过进程间通信 (IPC) 来实现服务调用。因此,需要一种机制将 Linux 系统调用重定向到微内核中的某个服务。

• 全局状态管理: Linux 内核使用全局数据结构来管理各种状态,例如文件描述符表。微内核缺乏这种全局数据结构,因此需要一种机制来管理这些状态,以便支持例如文件描述符复用和进程创建等功能。

鸿蒙内核的解决方案:

• ABI 兼容层 (垫片): HM 内核在内核空间中运行一个垫片,该垫片负责将 Linux 系统调用重定向到微内核中的适当服务。例如,文件系统服务负责处理与文件操作相关的系统调用。

• 集中式状态管理: 垫片充当集中式的状态管理器,负责管理全局状态,例如文件描述符表。这使得鸿蒙内核能够支持 Linux 中的许多关键功能,例如fork和poll。

• 特殊系统调用: 一些 Linux 系统调用,例如 ioctl 和 fcntl,扩展了系统 API,并允许驱动程序进行自定义扩展。鸿蒙内核将这些特殊的系统调用重定向到文件系统服务,该服务负责调用相应的驱动程序容器。

通过这个方案,鸿蒙内核通过了 AOSP 兼容性测试和供应商测试套件 (CTS/VTS) 的所有测试,证明了其与 Linux ABI 的兼容性。

大多数应用程序无需修改即可在鸿蒙内核上运行,但一些应用程序可能依赖于不稳定或某些非POSIX标准的API(例如epoll),这些应用程序无法在鸿蒙内核上运行。

2. 驱动容器

不可否认的是,Linux 拥有最丰富的设备驱动程序生态系统。

此外,有些驱动程序没有源代码,这给设备的适配工作带来了挑战。因此,复用 Linux 驱动程序对于鸿蒙内核的广泛部署至关重要。

鸿蒙内核采用 Linux Driver Container (LDC) 技术,在用户空间创建一个虚拟的 Linux 运行时环境,使得现有的 Linux 驱动程序无需修改即可在鸿蒙内核上运行。

LDC 重用了 Linux 内核代码库,并提供所有必要的 Linux 内核 API,允许驱动程序直接访问硬件设备。

鸿蒙内核会创建一个设备管理器,在虚拟文件系统 (VFS) 中创建文件节点,并将驱动调用通过 VFS 转发到相应的 LDC。

为了进一步提高性能,鸿蒙内核对 LDC 进行了改进,包括:

• 控制面和数据面分离: 将性能关键的 I/O 请求处理程序放在 LDC 之外,即原生驱动容器中的 twin driver,以减少用户空间和内核空间之间的切换开销。

• 并行处理: 通过并行处理虚拟内存区域 (VMAs) 的复制,加速 fork 调用的执行。

至于安全性,LDC 与鸿蒙内核中其他用户空间服务具有相同的威胁模型,并通过 SMMU 防止 DMA 攻击。

经过实践,LDC 能够复用超过 700 个 Linux 设备驱动程序,包括摄像头、显示屏、音频、NPU/GPU 和存储设备。

工程成本: LDC 只需要少量修改即可在鸿蒙内核上运行,大大降低了工程成本。

在下图中可以看到,通过控制平面和数据平面分离,LDC 在部分场景中提供了比 Linux 更好的性能。

其中“DC-twin”是结合了原生驱动容器中的twin driver达成的性能。但是由于twin driver需要额外的适配工作,通常只会在UFS驱动等核心硬件中采用这个方案,其它的外围硬件即使在没有twin driver的情况下也是可以驱动的,只是性能上有损失。

六、鸿蒙内核的表现

鸿蒙内核已经在数千万台设备上部署(原文"has been deployed"),包括智能路由器、智能汽车和智能手机等。鸿蒙内核针对不同的设备和场景需求,提供了不同的部署方案。

安全关键场景: 例如智能汽车和智能手机的 TEE(可信执行环境),鸿蒙内核优先考虑安全性和严格隔离,将所有 OS 服务部署在用户空间,并通过库提供 POSIX API。

性能关键场景: 例如智能手机,鸿蒙内核将性能关键的 OS 服务部署在内核空间,并与内存管理器合并,以减少 IPC 开销。

接下来是鸿蒙内核与Linux内核的性能测试成绩对比,测试在麒麟9000的设备上进行。

首先是LMbench,鸿蒙内核在上下文切换和网络通信方面比 Linux 快,内存操作性能相当,但 fork 操作仍然比 Linux 慢。

在实际负载中,fork 的主要开销来自复制虚拟内存区域(VMA)。它可以通过并行化来加速,从而将其开销从 150ms 降至 60ms(在典型应用中,Linux 的开销接近 30ms)。clone(创建线程)也比 Linux 慢 1 倍,这主要是由于多个操作系统服务(尤其是 IC2 中的驱动程序容器)与核心内核之间存在额外的 IPC。

其次是Geekbench 5.3.2测试成绩,可以看出两者的单核测试表现大致相当,但在某些场景上,鸿蒙内核有较大的优势。

应用冷启动时间,使用了Top 30的AOSP 应用比较冷启动时间(别忘了鸿蒙内核可以运行AOSP并且通过了CTS测试),总体结果是鸿蒙内核的应用冷启动时间比 Linux 短 17%,主要原因是鸿蒙内核的负载更轻,并且采用了自定义的调度策略。

典型场景负载,鸿蒙内核的负载比 Linux 总体低了 19%,这进一步证明了 HM 在性能方面的优势。

丢帧(俗称卡顿)次数,在持续24小时的典型使用场景测试中,得益于鸿蒙内核负载更轻,并且采用了自定义的 QoS 调度,鸿蒙内核的丢帧次数比Linux少了10%,稳定性更高。

中断延迟,在音视频场景下,中断延迟对用户体验至关重要。鸿蒙内核通过使用自定义的 "体验优先"策略,一次性执行所有的处理程序,将其延迟时间分别缩短了 10%(视频)和 65%(音频),而在 Linux 中,这些处理程序是在另一个额外的中断中处理的(由于 ARM GIC 的懒惰禁用问题)。

智能路由器和智能汽车: 在智能路由器中,鸿蒙内核将 OS 内存占用降低了 30%,允许连接更多客户端。在智能汽车中,鸿蒙内核将系统冷启动时间从 1.5 秒降低到 0.6 秒,并将跨域通信延迟从 250 微秒降低到 100 微秒。

七、经验总结

1. 兼容性优先,逐步原生化:

• 兼容性是商业化部署的关键,它确保了产品能够在不同平台间共享代码库,并支持第三方应用和驱动程序。

• 仅仅满足规范是不够的,需要通过大规模测试来验证兼容性,以发现潜在的问题。

• 可以先实现与现有生态系统的兼容性,再逐步改进性能和安全性,最终转向原生接口。

2. 部署先行,持续优化:

• 微内核很难一开始就满足所有的性能目标,需要进行全面的优化,包括框架和硬件。

• 部署可以促进多团队之间的合作,并验证可靠性。

• 应尽早开始部署,从小规模开始,逐步扩大。

3. 自动化验证是关键:

• 由于代码量的快速增长,完全形式化验证是不现实的。

• 可以使用半形式化验证和自动化验证来提高代码质量。

4. 规模效应导致的硬件故障/缺陷放大:

• 在大规模部署中,一些低概率的硬件故障或缺陷更容易发生,需要通过隔离、重启和监控等措施来缓解。

5. 大内核锁不适用于新兴场景:

• 在智能手机等新兴场景中,高频的系统调用和复杂的函数可能导致大内核锁,进而影响微内核的扩展性。

• 需要探索其他机制,例如异步 IPC 和细粒度锁,来提高性能和可扩展性。

八、总结和展望

鸿蒙内核是一个商业化的通用微内核,它维持了微内核的设计原则,同时提供结构支持,以应对新兴应用场景中的兼容性和性能挑战。它还有助于未来探索微内核在生产中的优势。例如,它的灵活性为适应 Linux 难以应对的日益增长的硬件差异化,以及实现容错以提高可用性提供了机会。

感言

翻译这篇论文使我学到了很多。虽然我是计算机专业出身,但是从学校毕业后,基本没有再看过计算机领域的论文。看这篇论文的过程中遇到很多闻所未闻的专业术语(例如垫片),让我大受震撼。

就文中提到的:鸿蒙内核已经能运行AOSP并通过CTS测试,只要华为愿意,可以在用户感知不到的情况下,把华为手机里运行的内核换成鸿蒙内核,而且兼容安卓生态(也许现在的HarmonyOS 4已经是这样了?)。不过我依然支持华为推进原生应用开发,别人的终究是别人的,掌握在自己手里才能让自己感到踏实,我相信华为在被制裁的五年里,对此感触颇深。