前言
在分布式调度框架中,关于轻量级服务管理的代码总体上可以分为四部分,分别是Samgr、Samgr_client、Samgr_endpoint及Samgr_server。本文是对Samgr部分的总体概述,相关代码文件位于distributedschedule_samgr_lite\samgr
。对于Samgr的分析思路是采用总体代码分析+系统功能管理器(Samgr_lite)的机制分析+子主题分析+n篇代码标注的方式进行技术分享。在本文中提到的数据结构或函数的详细分析可以在文末的附录中找到,其中也包含了许多优秀代码的分析。
Samgr总体分析
在samgr部分,可以将代码分为基础代码和核心代码。基础服务框架代码包括common、service、feature、iunknown、task等文件中的函数,主要的内容是提供一些鸿蒙业务模型的基本函数操作,通过这些定义的函数来完成业务运作。它们的概念通过子主题进行分析,链接在文末。核心代码在samgr_lite文件中,负责系统功能管理器(Samgr)的初始化。当Samgr初始化成功后,会对已注册的服务进行初始化。代码概述如下:
samgr_lite/
├── samgr
│ ├── adapter
│ │ ├── cmsis
│ │ │ ├── memory_adapter.c 实现CMSIS下的内存分配和释放函数,引用malloc.h文件
│ │ │ ├── queue_adapter.c 实现CMSIS下消息队列的创建和销毁以及元素出队和入队操作,底层实现依赖cmsis_os.h文件
│ │ │ ├── thread_adapter.c 实现CMSIS下与线程和互斥锁相关的操作函数,底层实现依赖cmsis_os.h文件
│ │ │ └── time_adapter.c 目前仅实现了获取当前时间的函数,底层实现依赖cmsis_os.h文件
│ │ ├── memory_adapter.h 声明内存分配和释放的函数,屏蔽POSIX和CMSIS内存操作的细节差异
│ │ ├── posix
│ │ │ ├── lock_free_queue.c 实现无锁队列的相关函数,包括队列的创建、元素入队及出队等函数
│ │ │ ├── lock_free_queue.h 定义无锁队列结构,并声明无锁队列的创建、判空、判满和入队、出队函数
│ │ │ ├── memory_adapter.c 实现POSIX下的内存分配和释放函数,引用stdlib.h文件
│ │ │ ├── queue_adapter.c 实现POSIX下消息队列的创建和销毁以及元素出队和入队操作
│ │ │ ├── thread_adapter.c 实现POSIX下与线程和互斥锁相关的操作函数,底层实现依赖pthread.h文件
│ │ │ └── time_adapter.c 目前仅实现了获取当前时间的函数,底层实现依赖time.h文件
│ │ ├── queue_adapter.h 声明消息队列的创建、销毁和元素入队、出队函数,屏蔽POSIX和CMSIS队列操作的细节差异
│ │ ├── thread_adapter.h 声明与线程和互斥锁相关的操作函数,屏蔽POSIX和CMSIS线程和互斥锁的细节差异
│ │ └── time_adapter.h 声明与时间相关的操作函数,屏蔽POSIX和CMSIS时间操作的细节差异
│ ├── registry
│ │ ├── service_registry.c 当前文件中未实现与业务注册相关的函数,均以弱引用的方式定义,在其他文件中有强引用的实现
│ │ └── service_registry.h 声明与业务注册相关的函数,包括服务接口的注册和查询、系统能力接口的注册和查询
│ └── source
│ ├── common.c 定义一个简化版的vector容器,包括创建vector、添加元素、查找元素、交换元素、获取元素个数等函数
│ ├── feature.c 实现功能(feature)对外接口的增加、查询、删除等操作
│ ├── feature_impl.h 声明与featureimpl对象相关的操作函数,包括增加接口、删除接口、获取接口等函数
│ ├── iunknown.c 实现与接口(iunknown)相关的操作,包括增加接口的引用、查询接口的信息、减少接口的引用
│ ├── message.c 实现与通信消息相关的操作,包括发送请求消息、发送响应消息、接收消息和释放消息等函数
│ ├── message_inner.h 定义消息体和消息类型,并声明消息接收和释放函数
│ ├── samgr_lite.c 实现系统功能管理(Samgr)模块注册、发现及初始化服务(service)、功能(feature)、对外接口(iunknown)相关的函数
│ ├── samgr_lite_inner.h 是对samgr_lite.h的补充,在samgr_lite.h中定义了一个SamgrLite结构体,用于注册/注销服务、注册/注销功能、注册/注销对外接口等。
在当前头文件中定义一个SamgrLiteImpl结构体,包含存储服务的容器以及任务池等信息。
│ ├── service.c 实现操作服务实例(serviceimpl)及其下属功能实例(featureimpl)的相关函数,包括创建服务实例、注册功能实例、停止服务实例等函数
│ ├── service_impl.h 定义服务实例结构体,并声明操作服务实例(serviceimpl)及其下属功能实例(featureimpl)的相关函数
│ ├── task_manager.c 实现任务池和消息处理的相关函数,包括创建任务池、启动任务池、引用/释放任务池、处理请求消息、处理响应消息等函数
│ └── task_manager.h 定义任务池结构体,并声明创建任务池、启动任务池、引用任务池等相关函数
Samgr_lite机制分析
Samgr是分布式调度中的重要成员,负责服务和功能的维护、注册和发现。鸿蒙系统的业务模型包含三大对象(详细介绍可以 看这里),分别是服务(service)、功能(feature)和对外接口(iunknown),所有的对外接口都要注册到指定的功能或服务的默认接口,而所有的功能都需要注册到相应的服务中,最后服务需要注册到Samgr中,经过初始化后服务开始向外提供功能。可以看到Samgr是注册和发现的核心。要想讲清楚Samgr的作用和机制就需要结合具体的服务,从服务的创建、注册和初始化等过程来分析它,这里以广播服务为例详细讲解它们的关联以及服务和Samgr从创建到启动的全过程。>看这里),分别是服务(service)、功能(feature)和对外接口(iunknown),所有的对外接口都要注册到指定的功能或服务的默认接口,而所有的功能都需要注册到相应的服务中,最后服务需要注册到Samgr中,经过初始化后服务开始向外提供功能。可以看到Samgr是注册和发现的核心。要想讲清楚Samgr的作用和机制就需要结合具体的服务,从服务的创建、注册和初始化等过程来分析它,这里以广播服务为例详细讲解它们的关联以及服务和Samgr从创建到启动的全过程。 首先我们需要自定义一个静态全局的广播服务,并赋值生命周期函数,它将会注册到Samgr中。当系统启动时,在Samgr部分从SYS_SERVICE_INIT(Init)函数进入服务初始化过程,在Init()函数中通过SAMGR_GetInstance()获取Samgr的系统能力管理类实例。在获取实例的过程中,若g_samgrImpl还未初始化,则触发它的Init()函数,初始化它的成员变量。然后调用系统能力管理类实例中的RegisterService函数,将广播服务注册到Samgr的服务集合中。这里就完成了广播服务的本地注册过程。
系统功能管理器(samgr)初始化完毕后。Samgr从SAMGR_Bootstrap(void)启动,这是系统功能管理器的启动入口。先更新Samgr的状态,从BOOT_SYS进入BOOT_SYS_WAIT状态,然后收集所有注册在Samgr中且处于SVC_INIT状态的服务,进入InitializeAllServices()函数初始化它们。服务初始化的整个过程可以分为三个操作。 第一,为服务绑定指定的任务池并启动任务池中的处理线程。服务必须要有自己的线程才可以执行业务逻辑,所以我们需要为它分配一个任务池,任务池中包含了线程以及对应的消息队列,服务接收并处理消息完成业务功能。每个服务创建的时候都会指定任务配置信息,根据不同的任务类型分配相应的任务池。任务类型有以下三种: 1.SHARED_TASK,根据service的优先级共享任务,此时绑定的是SamgrLiteImpl中维护的共享任务池。 2.SPECIFIED_TASK,为service绑定指定的任务池,遍历所有的ServiceImpl对象,查找相同任务配置的任务池,若找到则绑定,若未找到则创建新的任务池。 3.SINGLE_TASK,根据任务配置,创建新的任务池并绑定 第二,调用服务和功能自身的初始化函数。我们可以根据业务需要自定义服务和功能的初始化函数。 第三,向知名Endpoint注册服务和功能的信息,完成服务的远程注册过程。Endpoint是鸿蒙操作系统进程间通信的对外端点,用于标识进程的通信地址。如果进程间要进行通信,就需要知道目的进程对外的通信端点Endpoint的地址,而知名Endpoint就是我们人为设定的固定地址。所有的Endpoint都要向知名endpoint注册自己的地址,由知名Endpoint提供地址查询服务。
在服务的初始化过程中,当服务绑定任务池以后会有一个分叉。如果服务绑定任务池成功,那么它就有了对应的执行线程和消息队列。在进行初始化时,会将服务的初始化请求封装为Exchange对象,然后插入到对应的消息队列中,由对应的任务池线程接收并执行回调函数(用于初始化服务的函数)。如果服务绑定任务池失败,那么就在当前线程中直接调用服务的初始化函数。 当所有的系统服务初始化完成后,Samgr就从BOOT_SYS_WAIT进入BOOT_APP阶段。服务从SVC_INIT状态进入SVC_IDLE状态,此时服务可以正常执行业务功能。
总结
在本文中介绍了Samgr部分所有代码的概要信息,并且对Samgr_lite的运行机制和服务的注册及初始化启动过程进行了分析。服务的初始化过程涉及到两次注册(本地注册和远程注册)和任务池的绑定,并提到了关于进程间通信的部分概念。