赞
踩
在刚接触 Samgr 时笔者根本不知道其设计初衷是啥(水平较菜,刚接触C语言),尽管官方文档对其进行了介绍,但仍有种听君一席话胜读一席话的感觉。为此,笔者花了一天对其进行探究和学习,将笔记梳理并记录于本文。
参考博文
《鸿蒙子系统解读-分布式任务调度篇(上)》
《鸿蒙OS开源代码精要解读之—— 系统服务框架子系统(服务启动)》
《深入浅出OpenHarmony架构》
官方仓 https://gitee.com/openharmony/distributedschedule_samgr_lite 对其解释如下:
由于平台资源有限,且硬件平台多样,因此需要屏蔽不同硬件架构和平台资源的不同、以及运行形态的不同,提供统一化的系统服务开发框架。根据RISC-V、Cortex-M、Cortex-A不同硬件平台,分为两种硬件平台,以下简称M核、A核。
M核:处理器架构为Cortex-M或同等处理能力的硬件平台,系统内存一般低于512KB,无文件系统或者仅提供一个可有限使用的轻量级文件系统,遵循CMSIS接口规范。
A核:处理器架构为Cortex-A或同等处理能力的硬件平台,内存资源大于512KB,文件系统完善,可存储大量数据,遵循POSIX接口规范。
系统服务框架基于面向服务的架构,提供了服务开发、服务的子功能开发、对外接口的开发、以及多服务共进程、进程间服务调用等开发能力。其中:
面向服务的架构:
参考了一些资料后,发现一切皆服务的思想,与 Spring 中 Bean 类似,通过一种约定的封装方式调用相关类或者方法。增强了项目的扩展性,同时屏蔽底层实现,只关心接口。例如,可以通过获取serviceName 获取一个服务 service,并调用其接口相关方法。该 service 可以是从本地调用,也可以是从远程调用。
当意识到上述问题时,笔者对整个 samgr 的梳理就很顺畅了
参考资料的过程中,相关例子太少,都是开门见山地深入源码,对于笔者这种基础比较差的,着实看得痛苦。因此,在介绍具体细节前,找个本地直接调用的例子说明一下。(简化了代码,代码摘自《深入浅出OpenHarmony架构》)
服务的注册分为以下几个步骤
1 . 实现 Service 接口
2 . 创建静态对象
3 . 注册服务和缺省特性
static void Init(void)
{
SAMGR_GetInstance()->RegisterService((Service *)&g_example);
SAMGR_GetInstance()->RegisterDefaultFeatureApi(EXAMPLE_SERVICE,GET_IUNKNOWN(g_example));
}
SYSEX_SERVICE_INT(Init);
4 . 实现 Feature 接口
5 . 注册 Feature
static void Init(void)
{
SAMGR_GetInstance()->RegisterFeature(EXAMPLE_SERVICE,(Feature*)&g_example);
SAMGR_GetInstance()->RegisterFeatureApi(EXAMPLE_SERVICE,EXAMPLE_FEATURE,GET_IUNKNOWN(g_example));
}
SYSEX_FEATURE_INIT(Init);
服务/特性完成注册后,使用者可以通过SAMGR接口获取具体接口并进行调用
DemoApi *demoApi = NULL;
IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(EXAMPLE_SERVICE,EXAMPLE_FEATURE);
if(iUnknown == NULL){
return NULL;
}
int result = iUnknown->QueryInterface(iUnknown,DEFAULT_VERSION,(void **)&demoApi);
if(result !=0 || demoApi == NULL){
return NULL;
}
若能发现对应接口,则赋值给demoApi,故可进行对应接口 xxFunction 的调用
if(demoApi->xxFunction == NULL){
return NULL;
}
demoApi->xxFunction()
从简化的实例不难看出,想要使用OHOS,需要定义服务 service 和特性 feature ,feature 相当于主执行方法,一个 service 能够对应多个 feature ,一个 feature 也能对应多个 service 。更细节地使用指导不是本篇目的,对此不再将展开。可以见得,不就和 Spring 的 Bean 类似吗,通过 @Conponent 进行定义,再使用 @Autowired 进行自动装载。
safwk_lite 目录中是 foundation 这个 bin 文件的 main 函数,用于 samgr 启动,初始化所有注册的Service。即前一篇介绍 OHOS 启动流程的文章中,有提及 foundation 进程的拉起,该进程是用于提供 samgr 服务的。
可以看到 main 入口主要调用了 OHOS_SystemInit() ,其实际调用了 SAMGR_Bootstrap()
\distributedschedule\safwk_lite\src\main.c
void __attribute__((weak)) OHOS_SystemInit(void) { SAMGR_Bootstrap(); #ifdef DEBUG_SERVICES_SAFWK_LITE printf("[Foundation][D] Default OHOS_SystemInit is called! \n"); #endif } int main(int argc, char * const argv[]) { #ifdef DEBUG_SERVICES_SAFWK_LITE printf("[Foundation][D] Start server system, begin. \n"); struct timeval tvBefore; (void)gettimeofday(&tvBefore, NULL); #endif OHOS_SystemInit(); #ifdef DEBUG_SERVICES_SAFWK_LITE struct timeval tvAfter; (void)gettimeofday(&tvAfter, NULL); printf("[Foundation][D] Start server system, end. duration %d seconds and %d microseconds. \n",\ tvAfter.tv_sec - tvBefore.tv_sec, tvAfter.tv_usec - tvBefore.tv_usec); #endif while (1) { // pause only returns when a signal was caught and the signal-catching function returned. // pause only returns -1, no need to process the return value. (void)pause(); } }
具体地,SAMGR_Bootstrap() 调用 InitializeAllServices() 初始化服务。
void SAMGR_Bootstrap(void)
{
SamgrLiteImpl *samgr = GetImplement();
if (samgr->mutex == NULL) {
HILOG_INFO(HILOG_MODULE_SAMGR, "Samgr is not init, no service!");
return;
}
// --------------省略部分代码--------------
InitializeAllServices(&initServices);
VECTOR_Clear(&initServices);
int32 err = InitCompleted();
if (err != EC_SUCCESS) {
HILOG_INFO(HILOG_MODULE_SAMGR, "Goto next boot step return code:%d", err);
}
}
事实上,如前所述,本地调用的方式只需要在某个Service缓存中找到出需要调用的 Service。为什么还要大费周章进行 Service 初始化呢。事实上,如上述架构图所示,进程内服务之间除了可以直接调用(Direct use service),还可以通过消息进行异步调用。个人认为,Service 的初始化更多地是准备异步调用环境,因为异步调用需要利用消息队列+线程池的方式进行高效执行。
简单地说,如何实现一个异步调用。我们需要 Consumer 把需调用的函数封装成请求并发送给 Provider,由于请求会有很多,因此需要使用消息队列进行存储和管控。进一步地,需要利用线程池,不断地执行请求并返回调用结果。
对此,可以看到 InitializeAllServices 通过 AddTaskPool 为当前 service 创建了线程池 TaskPool 。最后通过 SAMGR_StartTaskPool 利用线程池中的线程执行轮询消息队列的任务
static void InitializeAllServices(Vector *services) { int16 size = VECTOR_Size(services); int16 i; for (i = 0; i < size; ++i) { ServiceImpl *serviceImpl = (ServiceImpl *)VECTOR_At(services, i); if (serviceImpl == NULL) { continue; } TaskConfig config = serviceImpl->service->GetTaskConfig(serviceImpl->service); const char *name = serviceImpl->service->GetName(serviceImpl->service); AddTaskPool(serviceImpl, &config, name); HILOG_INFO(HILOG_MODULE_SAMGR, "Init service:%s", name); InitializeSingleService(serviceImpl); } SamgrLiteImpl *samgr = GetImplement(); MUTEX_Lock(samgr->mutex); for (i = 0; i < size; ++i) { ServiceImpl *serviceImpl = (ServiceImpl *)VECTOR_At(services, i); if (serviceImpl == NULL) { continue; } const char *name = serviceImpl->service->GetName(serviceImpl->service); SAMGR_StartTaskPool(serviceImpl->taskPool, name); } MUTEX_Unlock(samgr->mutex); }
具体地,SAMGR_StartTaskPool 通过封装好的线程创建方法 THREAD_Create,将轮询消息队列这一任务 TaskEntry 让线程执行
int32 SAMGR_StartTaskPool(TaskPool *pool, const char *name) { if (pool == NULL) { return EC_INVALID; } if (pool->top > 0) { return EC_SUCCESS; } ThreadAttr attr = {name, pool->stackSize, pool->priority, 0, 0}; while (pool->top < pool->size) { register ThreadId threadId = (ThreadId)THREAD_Create(TaskEntry, pool->queueId, &attr); if (threadId == NULL) { HILOG_ERROR(HILOG_MODULE_SAMGR, "Start Task<%s, %d, %d> failed!", name, pool->stackSize, pool->priority); break; } pool->tasks[pool->top] = threadId; ++(pool->top); } return EC_SUCCESS; }
跟踪 TaskEntry 可以看到,确实在轮询消息队列,并对到来的消息进行处理。通过 SAMGR_MsgRecv 获取到 Exchange (ps : 这个在dubbo架构中也有相关分层设计,作用类似)。解析 Exchange 能够获得消息中想要请求的服务。进一步地
static void *TaskEntry(void *argv) { ServiceImpl *serviceImpl = NULL; THREAD_SetThreadLocal(argv); while (TRUE) { Exchange exchange; uint32 msgRcvRet = SAMGR_MsgRecv((MQueueId)argv, (uint8 *)&exchange, sizeof(Exchange)); if (msgRcvRet != EC_SUCCESS) { continue; } if (exchange.type == MSG_EXIT) { SAMGR_FreeMsg(&exchange); break; } serviceImpl = CorrectServiceImpl(&exchange, serviceImpl); BeginWork(serviceImpl); ProcResponse(&exchange); ProcDirectRequest(&exchange); ProcRequest(&exchange, serviceImpl); EndWork(serviceImpl, &exchange); SAMGR_FreeMsg(&exchange); } QUEUE_Destroy((MQueueId)argv); return NULL; }
ProRequest 实际上调用 DEFAULT_MessageHandle 进行请求的处理。可以看到,其主要会调用service.MessageHanle 和 service.feature.OnMessage 方法。这么分析过后,我们可以知道在定义服务的时候,每个成员函数应该实现怎么样的功能。而不是一上来就告诉你这个函数用来干啥干啥
void DEFAULT_MessageHandle(ServiceImpl *serviceImpl, const Identity *identity, Request *msg) { if (serviceImpl->serviceId != identity->serviceId) { return; } if (identity->featureId < 0) { if (serviceImpl->service->MessageHandle != NULL) { serviceImpl->service->MessageHandle(serviceImpl->service, msg); } return; } if (VECTOR_Size(&serviceImpl->features) <= identity->featureId) { return; } FeatureImpl *featureImpl = (FeatureImpl *)VECTOR_At(&(serviceImpl->features), identity->featureId); if (featureImpl == NULL) { return; } featureImpl->feature->OnMessage(featureImpl->feature, msg); }
GetFeatureApi 为服务发现接口,用于通过service 及 feature 名找到对应实现。可以找到 samgr 服务对应的 GetFeatureApi 实现如下
static IUnknown *GetFeatureApi(const char *serviceName, const char *feature)
{
ServiceImpl *serviceImpl = GetService(serviceName);
if (serviceImpl == NULL) {
return SAMGR_FindServiceApi(serviceName, feature);
}
FeatureImpl *featureImpl = DEFAULT_GetFeature(serviceImpl, feature);
if (featureImpl == NULL && feature == NULL) {
return serviceImpl->defaultApi;
}
return SAMGR_GetInterface(featureImpl);
}
具体地,DEFAULT_GetFeature 在缓存中寻找对应 feature。从此处看,似乎GetFeatureApi只能用于调用本地服务的 feature
FeatureImpl *DEFAULT_GetFeature(ServiceImpl *serviceImpl, const char *featureName)
{
if (serviceImpl == NULL || featureName == NULL) {
return NULL;
}
short pos = VECTOR_FindByKey(&(serviceImpl->features), (void *)featureName);
return (FeatureImpl *)VECTOR_At(&(serviceImpl->features), pos);
}
上述简单分析了samgr框架中,本地调用服务的过程。关于远程调用,如《深入浅出OpenHarmony》书中所言,主要涉及 IServerProxy,IClientProxy 及 LiteIPC 相关内容,本质应该就是 rpc 那一套。由于笔者对 rpc 实现比较熟悉,就暂不对其进行深入探究。挖个坑,以后有机会再来填
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。