《 Microkernel Goes General: Performance and Compatibility in the HongMeng Production Microkernel》论文中提到的一些性能优化的思路和方法是很有学习价值的,结合论文里提到的点,我做了个视频。为了方便大家观看,我梳理了这个文字版本。
读前提示:Linux是个通用操作系统,鸿蒙是特殊领域专用系统。在专属领域中,鸿蒙肯定会比Linux发挥得好。作为开发者,我们需要知道软件工程中的trade off,才不会被一些标题党带着走。
大家都知道Linux是个成熟的操作系统,那为什么还要重新造个鸿蒙出来呢?主要有两个原因:
‘PREEMPT_RT’(Real-Time Linux)
这个补丁前后花了10年才正式合入Linux。那么什么是微内核呢?简单来说就是内核只保留最基本的能力,比如进程调度、虚拟机内存、中断等,把一些应用放到了用户空间,比如驱动程序、文件系统等。这样系统服务与系统服务之间是隔离的,单个服务出现故障或者完全攻击,也不会导致整个操作系统挂掉,提高了操作系统的稳定性和可靠性。
论文中举了一个例子:Linux的代码中,驱动和文件系系统有3000多w行,占到了代码库的80%,过去4年的1000个通用漏洞披露中90%来自于这部分的代码。
但是微内核会有性能问题。以文件系统为例,和硬件设备交互就需要频繁切换到内核态,这样会带来性能损耗。那鸿蒙怎么解决这类问题呢?我们就来解读解读论文里提到的优化点。
中文可以翻译成 同步RPC样式的IPC快速通道 吧。
这个章节里提到了IPC(进程间)的通信在鸿蒙的场景中很频繁。在过程中很容易消耗大量的内存,为啥消耗内存?举个最简单的例子,AB之间进程之间通信,中间是不是有个通道,要不要耗内存?进程之间通信的时候,不能把主进程做的事阻塞住吧,那是不是要起线程去做,是不是要一些栈内存?
那鸿蒙是怎么优化呢?针对上述的栈,鸿蒙搞了个栈池,进程服务能在里面复用就复用,不够的时候就扩,当然它也会记录每个进程扩的量,扩多了肯定不给扩了,不然内存爆了。
可能有同学好奇,什么叫Fastpath,我们以RPC FastPath为例,看下常见的优化:
第二个是关于进程隔离级别的,有点像在传统的内核态和用户态之间加了一个中间态。
这一层适用于性能有要求而且已经验证的操作系统服务,当然这一层如果被攻击了,整个系统是要G的。
所以论文里提到,如果出现了新的攻击,鸿蒙是可以快速把IC1的应用退到IC2的。这也算是微内核带来的一个好处吧。
第三个叫Flexible Composition,则是参考宏内核的优化方式。将紧密耦合的OS服务合并,以减少高性能需求场景中的IPC频率(如下图左侧中间黄色部分,File System和Mem Mgr就合并了)。
我翻译成为基于地址令牌的访问控制。
这里面提到呢,一般微内核会将内核对象隐藏在内核后,通过权限控制访问。如果要对内核对象进行访问,就会涉及到内核态用户态的切换而带来的权限问题。而正是由于微内核最小化原则,某些内核对象(例如页表)的需要频繁地由内核之外的操作系统服务进行更新。
那鸿蒙是怎么优化的呢?每个内核对象被放置在HM的唯一物理页面上,然后通过权限配置只读、读写来映射给操作系统服务,避免总是经过内核降低性能。
这是一个关于Page fault的一个优化,那什么是Page fault?这里我们做一个简单的计算机基础知识回顾。
Page fault是指当程序尝试访问其虚拟地址空间中的一个页面,而这个页面并没有加载到物理内存(RAM)中时所发生的情况。当发生 page fault 时,操作系统会介入处理,并将所需的页面从磁盘上的交换文件或其它存储介质加载到物理内存中。
这个其实涉及到了计算机实现虚拟内存的关键机制之一,它们允许操作系统使用比实际物理内存更大的地址空间。在实际应用中呢,swap分区就是用来这个事情的。
那在鸿蒙的场景中呢。page fault会有一定的性能问题。主要原因在于从内核到分页器的额外往返通信。具体来说,在抛出页面错误异常后,内核会向分页器发出一次进程间通信(前面说到过这是一个OS服务),分页器检查地址并分配新的页面,然后返回内核更新页面表,最后回到应用程序。
总得看下来,异常处理是在微内核里做的,但是决策是在内核之外做的。那么鸿蒙做了一个改进,它在内核中保留了一个默认的页面错误处理机制——鸿蒙在内核中有段匿名内存的地址范围以及预先分配的物理页面(这段内存都是高性能程序要求的区域),那对于这部分内存,内核知道它可以立即映射到物理内存上,而无需进一步的决策过程。后面异步同步给内存管理器这个操作记录就行了。
如果页面错误发生在非性能关键区域(即不在内存管理器提供的匿名内存地址范围内),或者预先分配的页面已经被用尽,那么内核将不得不发送一个IPC请求给内存管理器来获取新的物理页面。这种情况下,内存管理器将根据其策略来分配新的物理页面,并通知内核进行相应的页面映射。
那总得来说呢,鸿蒙里面提到的性能优化手段还是比较精彩的。大多数基于work load来做出优化的,思路值得学习。
说完如何解决微内核的性能问题。我们继续讲解生态问题。
大家都知道写个操作系统比较容易,网上教程一搜一大把,但是如何让操作系统上的生态建立起来是个问题。
这里面做了个Linux ABI的兼容层,该层将所有Linux系统调用重定向到适当的OS服务。也就是说,其他Linux系统的二进制程序,理论上是可以直接兼容的。
而驱动这块,出于安全性的考虑是放在IC2的。但论文里面提到了一个有意思的方法,根据安全和性能要求,切分了驱动中的数据平面和控制平面。
对于驱动呢,鸿蒙会在IC1中另起一个程序,方便做隔离,两个程序之间是通过IPC来通信的,这两个程序分别来做控制平面和数据平面分离:控制平面负责管理、配置,并在较高层次上做出决策的部分;「数据平面则处理实际数据传输和处理任务的部分;前者对安全性要求高、性能要求低,后者反之。HM把设备驱动中「数据平面」(也就是IO操作)的部分,委托给IC1空间中的twin driver来完成,而控制平面操作留在LDC中。
总得来说,鸿蒙操作系统的诞生还是没有摆脱那句经典的话语——计算机世界总是在通用和专用之间摇摆。
当通用软件没法满足需求,而需求越来越多的时候呢,则会出现对应的专用软件。