前言
本文是对分布式任务调度之系统服务管理中的一些基础框架、数据和操作总体概述,包含底层内存管理、底层队列操作、底层线程管理、时间操作、令牌桶操作、vector操作、系统功能存储结构等。这也是Samgr部分中的最后一个子主题。它们作为底层操作为上层服务和功能间的交互提供相应的支持。分析思路采用1篇总体概述+n篇代码标注的方式进行技术分享。在本文中提到的数据结构或函数的详细分析可以在文末的附录中找到,其中也包含了许多优秀代码的分析。
基础框架适配层概述
鸿蒙操作系统的设计初衷是实现万物互联,使用同一套系统能力即可适配多种终端形态。由于平台资源有限且终端底层硬件的多样化,因此需要屏蔽不同硬件架构和资源的差异,提供统一化的系统服务开发框架。鸿蒙系统的设计将种硬件平台划分为两类,简称为M核、A核。
M核:处理器架构为Cortex-M或同等处理能力的硬件平台,系统内存一般低于512KB,无文件系统或者仅提供一个可有限使用的轻量级文件系统,遵循CMSIS接口规范。
A核:处理器架构为Cortex-A或同等处理能力的硬件平台,内存资源大于512KB,文件系统完善,可存储大量数据,遵循POSIX接口规范。 注:M核和A核的介绍摘自鸿蒙系统的简介。
综上所述,不同硬件的底层差异主要是来源于它们所遵循的接口规范,所以鸿蒙操作系统通过实现统一的内存管理、线程管理、队列管理等来屏蔽这种差异,最终实现一套系统能力适配多种终端设备。
内存管理
在POSIX和CMSIS中底层内存分配和回收的差异主要在于引用头文件的不同。在POSIX下,malloc()
和free()
在stdlib.h
中,而CMSIS在malloc.h
中。屏蔽它们的差异是通过定义一个统一的内存分配和回收的头文件,然后不同的平台进行差异性实现。在不同平台编译时,内存分配和回收的接口是不变的,我们就不需要改上层代码。
队列相关操作
鸿蒙操作系统对于POSIX和CMSIS两类平台在队列的实现上也存在差异,也是通过在头文件中定义统一的队列操作接口,然后进行不同的实现。在CMSIS下,鸿蒙系统的队列相关操作是通过封装cmsis_os.h
中的队列操作来实现的,如创建队列osMessageQueueNew()
、销毁队列osMessageQueueDelete()
。在POSIX下,队列的操作也经过二次封装,不同的是底层的创建队列、销毁队列、元素入队、元素出队等操作都是自行实现的一个无锁结构。
线程管理
对于线程部分的实现,POSIX下使用pthread.h
,CMSIS下使用cmsis_os.h。这部分包含互斥锁的操作和线程的操作,这里介绍一下两个比较有意思的设计。第一个是定义了一个用于记录线程总数的全局变量,它的作用是统计已申请的线程总数,用于维护全局的资源调配。若申请数已达上限,则拒绝创建新的线程。第二个是线程的私有变量g_localKey,它是所有线程可访问的,各个线程可根据自己的需要往g_localKey中填入不同的值,相当于提供了一个同名而不同值的全局变量。在鸿蒙系统消息机制的设计中,线程会将消息队列的首地址保存在自己的私有变量中,当CPU调度到指定的线程时,从中取出消息队列首地址的值,然后这个线程就开始处理消息队列中的消息。这个变量实现了消息队列与线程的绑定。
时间相关操作
在分析与时间相关的操作时,发现代码中只实现了获取当前时间的功能,其余函数都是默认返回失败。对于获取当前时间,POSIX通过time.h
实现,CMSIS通过cmsis_os.h
。
基本数据及功能概述
令牌桶相关操作
鸿蒙操作系统通过自定义的令牌桶来实现消息处理的流控机制。以恒定的速率来产生令牌,在进行消息的分发前先检查是否由足够的令牌数,若令牌数不足说明消息处理速率过快,则拒绝执行。
容器相关操作
定义简化的vector类,它在鸿蒙系统代码中被频繁的使用,作为数据的存储容器。它适用于数据量较小且需要动态扩展的C语言开发场景,底层实现了自动扩容机制。这里分析一下它对于内存管理的设计优点。
第一个优点,虽然它作为数据的存储容器,但是它本身并不存储数据的值,而是通过二级指针data指向一块动态缓冲区,这块缓冲区中存储指向数据的指针。这样就可以动态的调整data指向的缓冲区大小而不影响vector对象。进行自动扩容时,只需要重新申请一块缓冲区,并拷贝旧缓冲区中存储的指针值,不需要拷贝实际的数据对象,这样可以大大的降低扩容时的拷贝量。拷贝指针值只有指针大小,一般是4或8字节,而拷贝实际数据是结构体的大小,远不止几个字节。
第二个优点,当我们向vector中添加新的数据,若插入的位置大于等于最大容量max时,优先使用已释放的数据指针空间,从后往前遍历寻找。若不存在已释放的数据指针空间,再进行自动扩容。这种方式实现了空间的最大化利用,有利于内存资源小的设备,但是对于内存资源充足的设备并且数据条目极多的情况下,遍历会带来更多的性能损耗。
Vector结构定义如下:
typedef struct SimpleVector {
//可存储的最大数据记录数
int16 max;
//存储的数据记录数量的峰值
int16 top;
//已释放的数据记录数
int16 free;
//记录数据的缓冲区
void **data;
//将数据元素转换为用于比较的键
VECTOR_Key key;
//比较key1和key2的大小。
VECTOR_Compare compare;
} Vector;
系统功能存储结构
SAStore是实现进程间通信的重要存储结构,在它的内部维护和记录了参与进程间通信的所有服务和功能的信息以及对应的通信访问地址。它的root字段指向一个双向链表,链表的每一个结点都保存了一个服务的相关信息。而在服务信息结点中还包含了一个由功能信息组成的双向链表。
总结
本文对分布式调度框架中的基础数据结构和操作进行了讲解,并且分析了部分操作在实现上的优点。它们是整个模块中的基础操作,通过它们来屏蔽底层的差异性,完成复杂的逻辑操作。针对不同的平台进行差异化的实现,然后提供统一的函数接口,保证上层代码与底层平台解耦和,达到一套系统能力适配多种平台设备的目标。