以广播服务为例——Service和Feature的注册

本篇文章以广播服务为例讲解SA框架中系统服务及其Feature的注册

0. Broadcast机制介绍

广播机制多用于不同进程间的消息通讯
一般来说有三种角色:

  • consumer:接受数据和事件的模块
  • provider:事件和数据的提供者
  • subscriber:通过接口广播某topic下的事件和数据

Topic一般用于区分不同类型的事件和数据,在鸿蒙中topic定义的是无符号的32位整型
在这里插入图片描述
一般的广播机制:(以Android为例)广播的发送者和接收者事先是不需要知道对方的存在的——这样系统的各个组件可以松耦合的组织在一起,使系统具有很高的可扩展性,容易集成。一般分为两步:

  • 第一步通过广播类型(Topic),consumer注册接受类型
  • 第二步publish,通过implementInterface,provider向subscriber发送需要广播的信息和数据,subscriber通过关键字查找广播的对象,将广播发送到对应的consumer。

典型的发布/订阅模型,广播发送者和广播接收者的执行是异步的,发出去的广播不会关心是否有receiver接收,也不确定receiver何时收到做如何处理

在鸿蒙中是如何实现的呢?请看下图:
在这里插入图片描述
在OpenHarmony中多加入了Samgr管理subscriber,由provider发布事件或数据给订阅了该主体的consumer进行handle或response,而subscriber来管理topic的订阅和变更,samgr则更多的充当一个管理和登记的中转站


介绍完广播机制后,我们来看看广播服务的发布函数publish是如何工作的:

1. Publish——广播服务的发布函数

1.1 Publish——广播发布

在这里插入图片描述
函数流程:
1. 首先调用GET_OBJECT获取一个广播的实例provider
2. 获取其feature
3. 检查request是否非空
4. 将对应数据拷贝入request中
5. 调用ImmediatelyPublish根据topic进行对应consumer的广播


1.2 ImmediatelyPublish——立即传输

对于调用的ImmediatelyPublish

函数流程:首先根据topic的类型得到需要发送的对象队列---->遍历consumerList,如果有identity为null则将needAync置为true并不对它执行SAMGR_SendSharedDirectRequest---->对于其他节点调用SAMGR_SendSharedDirectRequest进行请求的广播(这里调用了DefaultHandle)---->存在身份为空的节点时,还需要另外调用SAMGR_SendSharedRequest


1.3 exchang结构体——封装广播的载体

调用SAMGR_SendSharedDirectRequest和SAMGR_SendSharedRequest时使用了exchange结构体用于封装广播需要的信息
在这里插入图片描述


1.4 SendSharedDirectRequest与SendSharedRequest——同步和异步发送

那么为什么需要这两种方式的请求发送呢?

首先我们从函数的原型上来看:

int32 SAMGR_SendSharedDirectRequest(
const Identity *id, 
const Request *req, 
const Response *resp,
 uint32 **ref,                                 
 Handler handler)

uint32 *SAMGR_SendSharedRequest(
const Identity *identity, 
const Request *request, 
uint32 *token, 
Handler handler)

第一个传入了identity,request, response, ref 和handler
第二个传入了identity,request, token, handler

再从函数的调用来看:
在这里插入图片描述
在这里插入图片描述
在实际使用中SAMGR_SendSharedRequest并没有传入handler

从函数publish的上下文来看
SAMGR_SendSharedDirectRequest作用于链表的遍历中,需要有同步的response进行答复并且调用了DefaultHandle进行另一个互斥锁的申请与释放以及consumer的notify

而SAMGR_SendSharedRequest,从参数列表中可以看到没用response,也没用使用默认的handle,而是当needAync(话说异步的缩写不应该是async)的时候调用,异步这里指的就是request之后的response不需要立刻应答,所以参数中没用response也合理

再从两者本身的代码出发
它们都使用了exchange作为信息的载体,传入SharedSend进行真正的消息发送


1.5 函数流程图

在这里插入图片描述


之后我们以广播服务为例讲讲系统服务和feature是如何在SA中注册

2. Service的注册(以广播服务为例)

那么我们直接从源码开始

2.1 Init——服务注册初始化

广播服务初始化注册的代码只有一行:
在这里插入图片描述
没错!只有一行!鸿蒙就是这么硬核,但是别小看了一行代码,注册服务的功能的玄机就在这一行代码中

这里涉及了重要的结构体g_samgrImpl,我们看看该结构体的各个属性:

struct SamgrLiteImpl {
    SamgrLite vtbl;//用于实现samgrLite中的函数指针
    MutexId mutex;//互斥锁序号
    BootStatus status;//状态
    Vector services;//服务向量
    TaskPool *sharedPool[MAX_POOL_NUM];//任务线程池
};

那么我们也能够理解SAMGR_GetInstance()在具体干什么:

当g_samgrImpl没用初始化时,调用Init对g_samgrImpl结构体的各属性进行初始化赋值并返回g_samgrImpl.vtbl

这里简单阐明SamgrLiteImpl 各个属性的作用

  • SamgrLite vtbl:通过init初始化后函数指针与对应的实现指针关联,就能够通过vtbl中的函数对service、feature和api进行注册、注销和获取API等操作
  • MutexId mutex:互斥锁,保证多线程下共享数据的同步,它的实现取决于平台使用的内核,M核使用cmsis实现,A核使用posix(可移植操作系统接口)进行实现——从SAMGR_GetInstance()中我们也可以看到是根据SamgrLiteImpl中互斥锁是否为NULL来判断是否需要初始化,也可以说每一个SamgrLiteImpl对应唯一的mutexId
  • BootStatus status:service启动的状态对应
    在这里插入图片描述
  • Vector services:samgr通过vector管理所有注册的子服务(通过samgrImpl来关联具体的service和feature)
  • TaskPool *sharedPool[MAX_POOL_NUM] :为每个service创建的taskPool资源,通过GetTaskConfig()返回每个service运行起来的task参数配置

2.2 RegisterService——服务注册

看到该行初始化代码的后半段:SAMGR_GetInstance()给g_samgrImpl初始化后返回了vtbl并调用了其中的函数RegisterService

    SAMGR_GetInstance()->RegisterService((Service *)&g_broadcastService);

然后我们看向最重要的登记服务函数
在这里插入图片描述
函数流程:首先调用GetImplement返回一个samgr实例---->通过VECTOR_FindByKey匹配samgr已登记的service与新加入的服务是否重复——因为服务都是单例模式,所以这里需要查找---->找到(pos>=0说明存在该服务)则释放互斥锁终止服务登记---->没找到则检查最大服务支持---->调用SAMGR_CreateServiceImpl创建一个serviceImpl---->调用VECTOR_Add为新建的serviceImpl获取新的serviceId

实际落实到广播服务:传入广播服务的g_broadcastService进入RegisterService(SAMGR_GetInstance只执行一次,如果起先已经存在g_samgrImpl则不需要再次初始化)为广播服务创建一个新的ServiceImpl并获取id添加到VECTOR管理队列中

广播服务Service的注册就已经完成了,最后同样附上一张函数流转图供读者参考学习
在这里插入图片描述


2.3 ServiceImpl——服务管理实例

上面我们看到init中返回了一个全局变量g_samgrImpl其中有个属性为Vector services——其中存储的就是其需要管理的ServiceImpl的标识,而SA框架就是通过一个个ServiceImpl来管理系统或APP服务和feature的。所以这里我们需要再了解下该结构体的具体内容

首先我们看看该结构体长什么样:
在这里插入图片描述
在g_samgrImpl全局变量的services Vector中记录了每个服务实例ServiceImpl的指针,并不直接记录和管理service和feature本身,在函数RegisterService中我们可以看到其存储的每个Impl的serviceId:
在这里插入图片描述
调用函数VECTOR_Add完成两件事:

  1. 首先将serviceImpl的指针存入g_samgrImpl.services.data[i]中,然后返回对应的index
  2. 将返回的index存入serviceImpl.serviceId作为后面取出serviceImpl的一个id属性

然后我们看回ServiceImpl中包含的属性:

  • Service*service :指向当前ServiceImpl对应的具体service对象
    这里出现的Service是所有service的服务,每个系统服务或者用户自定义的服务都需要继承其初始化的四个指针函数

而其他结构体继承时,使用#define INHERIT_SERVICE,将四个函数指针加入服务结构体中并编写函数进行函数实现——实现类似Java的interface的实现或者说是Java抽象函数的实现(不知道这样形容恰不恰当)——也能算是在C中实现C++对象继承的类似功能

  • IUnknown *defaultApi :继承IUnknown结构的service或者feature,都会有对应的三个接口函数指针
//简单阐明三个函数指针的用途
struct IUnkonwn{
	//实现父类指针导具体子类对象service/feature指针的类型转换,获取子类提供的功能和服务
    int (*QueryInterface)(IUnknown *iUnknown, int version, void **target); 
    //记录service/feature的引用数量
    int (*AddRef)(IUnknown *iUnknown); 
    //释放对应的IUnknown接口                                    
    int (*Release)(IUnknown *iUnknown)
}
  • TaskPool *taskPool:我们可以回想起在g_samgrImpl的属性中也有TaskPool的创建,关于任务池创建和类型、g_samgrImpl和serviceImpl的TaskPool的关系的阐明我们放在后续讲解中
  • Vector features:每一个service会对应0个、1个或者多个feature,feature需要依赖service才能运行,但是这里仅仅是使用Vector来管理feature,类似g_samgrImpl通过Vector管理serviceImpl是一个道理,后面利用broadcast服务的pubsubfeature的注册为例讲解feature注册时会再次回顾这个机制
  • int16 serviceId:在上面RegisterService中可以看到serviceId记录的是serviceImpl对象指针保存在g_samgrImpl.services.data中的index
  • uint8 inited:对应了三个状态:
    在这里插入图片描述
    SVC_INIT表示初始化
    SVC_IDIE表示空闲中
    SVC_BUSY表示service正在处理消息事件
  • Operations ops:保存了一些状态信息
    在这里插入图片描述
    需要注意的是这里的step实际上是整个系统的bootStatus
    在这里插入图片描述
    而这些状态也对应了整个服务的三个步骤:系统服务的注册---->用户APP服务的注册---->SAMGR启动所有服务初始化
    BOOT_SYS状态对应了系统服务的注册
    BOOT_APP状态对应了用户APP服务的注册
    BOOT_DYNAMIC对那个了SMAGR启动所有服务的初始化状态
    中间的WAIT的状态是一个过渡态
    每个阶段的任务完成后对应的系统状态就会切换,直到进入BOOT_DYNAMIC状态,就进入一个平稳运行的状态

至此服务注册的全部内容完毕!

而一个Service会对应零个,一个或多个feature以协助服务来完成其功能。而对于广播服务而言,其由一个feature实例PubSubImplement,所以也需要feature的注册

3. Feature的注册(以广播服务为例)

3.1 g_broadcastFeature

跟service类似,也有一个全局变量用于管理broadcast的feature,里面将五个函数指针初始化指向五个函数
在这里插入图片描述


3.2 g_pubSubImplement

PubSubImplement类型的全局变量g_pubSubImplement
其具体的初始化在pub_sub_implement.c中:
在这里插入图片描述
细心的同学可以发现这里注册的Publish就是上面解析的Publish函数
由此我们可以再一次证明service的部分能力是通过feature来实现的——比如broadcast服务的广播服务能力就是通过PubSubImplement内的PubSubFeature中的Publish进行实现

接下来正式进入feature的登记和初始化过程


3.3 Init()——feature的注册

函数功能:PubSubFeature的注册
在这里插入图片描述

函数流程:
1.首先将全局变量g_broadcastFeature引入用feature指向它
2. 对于relation属性进行初始化---->给feature的互斥锁进行初始化
3. SAMGR_GetInstance()->RegisterFeature进行feature的登记(这里的SAMGR_GetInstance用于返回GetImplement()->vtbl)
4. 通过BCE_CreateInstance将新建的g_broadcastFeature加入g_pubSubImplement并使用apiEntry指向
5. 最后RegisterFeatureApi进行featureApi的登记和初始化

后面我们来看涉及的两个主要函数RegisterFeature和RegisterFeatureApi


RegisterFeature
在这里插入图片描述
函数流程:
1.首先通过IsInvalidFeature判断feature是否存在,存在则无需注册返回false
2. 调用GetService返回参数serviceName对应的serviceImpl
3. 调用DEFAULT_GetFeature判断返回的serviceImpl是否存在所给的feature,存在则不需要注册
4. 最后调用DEFAULT_AddFeature通过featureImpl建立起feature和serviceImpl的联系


RegisterFeatureApi
在这里插入图片描述
函数流程:
1.首先通过IsInvalidIUnknown判断传入的publicApi(对于broadcast的featureApi来说传入的是GET_IUNKNOWN(apiEntry)其中apiEntry是PubSubImplement类型)是否实现了iunknown的三个函数指针所需要的函数,没有实现则返回false
2. 通过GetService得到serviceName对应的serviceImpl
3. 当传入的feature参数为NULL时(有些服务没有feature),则将传入的publicApi赋给serviceImpl->defaultApi,没有feature则通过服务实体来提供API能力
4. 存在feature则通过DEFAULT_GetFeature得到featureImpl
5. 最后通过SAMGR_AddInterface将featureImpl->iUnknown = publicApi

3.4 feature注册流程图

下面放上以上分析的图解供读者参考
在这里插入图片描述
至此feature的注册讲解完毕!

4. 注册service和feature的四条总结

最后有四条总结:

1. 必须先注册初始化service才能注册初始化对应的feature

2. service通过全局变量g_samgrImpl管理所有的ServiceImpl,一个服务对应一个ServiceImpl,一个ServiceImpl对应一个service结构体

3. 一个ServiceImpl对应0个、1个或多个feature

4. g_samgrImpl中的Vector中通过Vector管理所有的服务和feature,通过Vector的操作函数,能够根据name或id取得ServiceImpl和FeatureImpl,再对具体的service和feature进行相应的操作

5. 有feature的service可以通过feature来提供服务——比如broadcast服务通过PubSubFeature实现Publish服务

6.一个系统/用户服务初始化完成的三步走:
通过SYS/APP_SERVICE_INIT进行服务的注册
通过SYS/APP_FEATURE_INIT进行feature和featureApi的注册(如果存在feature)
通过SAMGR_BootStrap函数将服务初始化,分配taskPool并初始化messageHandle

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家一级假勤奋研究牲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值