先来看看HarmonyOS的技术架构:
HarmonyOS当前分为以下几种系统类型:轻量系统 、小型系统、标准系统。针对不同量级的系统,分别使用了不同形态的内核,在轻量系统上,可以选择LiteOS-M;在小型系统和标准系统上,可以选用LiteOS-A;在标准系统上,可以选用Linux。
总的来说,在轻小型系统中,HarmonyOS所使用的内核为LiteOS,在标准系统中使用Linux。
而目前Huawei LiteOS支持多种芯片架构,如Cortex-M series、Cortex-R series、Cortex-A series等。LiteOS有两个应用方向,分别是LiteOS-M和LiteOS-A。
LiteOS-M适用于轻量级的芯片架构,面向的MCU一般是百K级内存,例如cortex-m、riscv32;而LiteOS-A则适用于小型的芯片架构,面向设备一般是M级内存,例如cortex-a。我使用的hi3861v100,芯片架构为riscv32,所适配的就是LiteOS-M内核。
这是官方文档中的一段描述:
所以接下来要学习的暂时是LiteOS-M。详见HarmonyOS/文档/指南/入门。
首先看看LiteOS-M的内核架构图。可以看到应用于轻量级内核的LiteOS-M整体的架构非常简单,底层支持的内核目前只有Cortex-M和RISC-V系列,在此之上抽象出统一的硬件架构。以该硬件架构为基础,加入OS内核和拓展模块,再继续将这两部分抽象出来,适配到CMSIS和POSIX两个标准上,这样在应用层所编写的程序,就可以具有良好的移植性和可拓展性。
那么这个架构在程序中是如何体现的?上一篇鸿蒙硬件开发:DevEco Device Tool V2.1正式版以及windows平台编译链的更新中,已经下载了HarmonyOS的源码并成功运行了一个程序。根据这个工程的目录结构和内容来分析一下。
可以看到有一个kernel文件夹,其中存放着的就是LiteOS的两个分支。
liteos_m分支的目录结构如下:
在README中可以看到对各个文件夹功能的描述。
├── components # 可选组件
│ ├── cppsupport # C++支持
│ └── cpup # CPUP功能
├── kal # 内核抽象层
│ ├── cmsis # cmsis标准接口支持
│ └── posix # posix标准接口支持
├── kernel # 内核最小功能集支持
│ ├── arch # 内核指令架构层代码
│ │ ├── arm # arm32架构的代码
│ │ └── include # 对外接口存放目录
│ ├── include # 对外接口存放目录
│ └── src # 内核最小功能集源码
├── targets # 板级工程目录
├── utils # 通用公共目录
那么根据上方的LiteOS-M架构图,一层一层分析。
姑且这么叫。在官网可以看到对这部分的定义。可以看到现有的工程已经对特定的架构进行了适配支持。
那么在文件夹中查看,可以看到 /kernel/liteos_m/kernel/arch 目录下对risc-v和arm两种架构的实现。
risc-v中实现了riscv32架构。
而arm中实现了四种cortex架构。
可以看到这些架构都按照上图的命名规则进行了实现。那么接下来的OS内核的实现就可以通过这个统一的接口来对底层的硬件内核进行操作,从而在OS层实现了对硬件架构的屏蔽作用。
还是姑且这么叫吧,我随便起的。
基本上所有的OS都会有的一些东西,包括事件、队列、信号量、互斥量、线程/任务等等。这些模块的具体实现文件在 /kernel/liteos_m/kernel/src 中,如下图。
这就是最开始那张架构图中的基础内核部分,实现了LiteOS的核心内容。
当然,在 /kernel/liteos_m/components 中,还有一些可裁剪的模块:文件系统、网络、调试追踪……。也就是那张图中在基础内核旁边的拓展模块。
作为一个OS,势必会存在任务调度等需要操作底层寄存器的地方,其所依赖的就是上方对于硬件架构所抽象出的那些文件中的函数或代码段,例如上下文切换、中断系统等。虽然对底层的硬件架构做抽象适配会耗费一些时间和资源,但是这带来的就是在上层开发时不需要关心底层架构的不同,从而能够使用一套统一的接口来对寄存器进行操作。
内核抽象层,终于有正经名字了。这一层的目的其实跟硬件架构抽象层差不多。硬件架构抽象层是为了消除一个系统中硬件架构的不同而将其抽象出统一的接口,KAL层是为了让应用层在不同的操作系统上实现统一的API调用体验而产生的。这么说好像有点绕,在代码里看一眼其实就很容易理解了。
在刚开始学LiteOS的时候,可以发现,其实LiteOS本身的API都是以 LOS 开头的,例如下图是官网对LiteOS-M的任务接口介绍。
随便找一个内核文件查看。
都可以发现其实LiteOS的API都是这种格式。例如其中的对任务的API和数据结构:
LOS_TaskCreate(UINT32 *taskID, TSK_INIT_PARAM_S *taskInitParam); //创建一个新任务
TSK_INIT_PARAM_S //任务初始化结构体
刚看完LiteOS教程的时候,我就有这个疑惑:在上一篇中的第一个程序中,可以看到,对任务初始化的结构体变成了 osThreadAttr_t ,而创建新任务的API变成了 osThreadNew() ,明明hi3861是riscv32的架构,使用的就是LiteOS-M,为什么这里的API不是LiteOS的API呢?
这就是KAL层的作用了。在官网的文档中可以看到这样一段:
讲的其实已经很清楚了,目的就是这句话:
它包含多个组件层,其中之一是RTOS层,该层定义了一套通用及标准化的RTOS API接口,减少了应用开发者对特定RTOS的依赖,方便用户软件的移植重用。
至于这明明是给cortex创建的标准,为什么riscv32的架构也用这套?如下:
总之,从功能上看,实现这套标准的目的,说的简单一点,就是为了让我们在基于cortex内核(riscv?)使用操作系统的情况下,所编写的应用层的代码,不会因为操作系统的变更而大量的修改OS部分的API。
举个例子,如果我使用rt-thread编写了一套应用层代码,其中使用的都是 rt_thread_create() API来创建线程,现在我想在LiteOS上实现同样的功能,加入逻辑上不需要修改,但是这显然需要将文件中所有对rt-thread的API都换成LiteOS的,例如创建线程的 rt_thread_create() 都要换成 LOS_TaskCreate() ,还有其他所有的OS有关的API,都需要替换。
但是如果我在使用两个操作系统之前,都做了类似KAL层的抽象操作,都把它抽象成 osThreadNew() ,那么我的应用代码就可以原封不动的直接照搬,移植到另一个操作系统上,这就是KAL层的作用。
上图中表示HarmonyOS LiteOS-M仅提供其版本2的实现,我们可以在代码中看看具体的实现方法。
同样在liteos_m的文件目录下,可以看到 kal/cmsis 文件夹下,有着liteos对cmsis定义的os的实现。
在 cmsis_os.h 中,明确表示了liteos暂时不支持cmsis版本1。
而在 cmsis_liteos2.c 中,可以看到大部分的liteos的适配,其中就有上方的例程中所使用的线程创建API osThreadNew() 。在这个函数里面就有着LiteOS的原生API了。
那么其实很容易发现,这其实就是对LiteOS的原生API再进行一次封装,封装出的API是一个cmsis对于OS的标准API格式,也就是这个 osThreadNew() 。
那么之后所有的对于LiteOS的操作,基本都可以使用这个KAL层所提供的API来进行操作了。