[文章]Hi3516的SAMGR--系统服务框架子系统-8 client EP的注册

阅读量0
0
0
文章转载自:liangkz

我们接着前文《Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动》,继续分析wms_server进程后继的启动步骤和注册EP的流程,从DEFAULT_Initialize(ServiceImpl *impl) 函数入口开始。

从前文可以知道,wms_server进程启动了三个service:Broadcast、WMS和IMS,Broadcast服务有一个feature:“Provider and subscriber”,另外两个服务,没有feature。每个service都有自己的消息队列,都会收到InitRequest消息,通过HandleInitRequest()函数,service会带着自己的feature跑一遍DEFAULT_Initialize(serviceImpl)函数。

//foundation/distributedschedule/samgr_lite/samgr/source/service.c
DEFAULT_Initialize()内,很明显可以分为以下四步的:
针对service跑前两步
  • [4-1] impl->service->Initialize(impl->service, id)
      这一步会调用service自己生命周期的 Initialize() 函数来做初始化。
  • [4-2] SAMGR_RegisterServiceApi(serviceName, NULL, &id, impl->defaultApi)
      这一步轻量系统执行空函数,没有实际动作;小型系统执行remote_register.c 中定义的函数,注意参数。

针对service所有的feature,for循环让每个feature都执行一遍[4-3]和[4-4]两步,service没有feature,就不用执行这两步:
  • [4-3] feature->feature->OnInitialize(feature->feature, impl->service, id)
      这一步会调用feature自己生命周期的 OnInitialize() 函数来做初始化。
  • [4-4] SAMGR_RegisterServiceApi(serviceName, featureName, &id, feature->iUnknown)
      这一步调用的API与第2步调用的是同一个API,但是注意参数的变化,特别是第四个参数。

//foundation/distributedschedule/samgr_lite/samgr_client/source/remote_register.c

我们详细看一下SAMGR_RegisterServiceApi()的工作,它内部,也很明显可以分为以下三步的:
  • [3-1] InitializeRegistry()
每一个进程,都有一个全局的RemoteRegister g_remoteRegister,本进程的所有services中,第一个跑到这里的service会去初始化这个 g_remoteRegister,主要是创建互斥信号量、创建一个空的Vector clients、创建本进程的通信终端endpoint。以后的service/feature再跑进这一步时,基本上都会因为已经存在endpoint了而就此退出(特定条件下会清空本 g_remoteRegister,重新生成,这里先不管)。
我们先把关注的重点放在创建 EP上。

//foundation/distributedschedule/samgr_lite/samgr_endpoint/source/endpoint.c
所有进程都会通过SAMGR_CreateEndpoint("ipc client", NULL) 创建一个名为"ipc client"的EP,只有管理者会创建名为"samgr"的EP,这个后面讲。

SAMGR_CreateEndpoint()内会初始化如下一些参数(没列出来的先略去):

endpoint->context = OpenLiteIpc(LITEIPC_DEFAULT_MAP_SIZE);  

        endpoint->boss   = NULL;

        endpoint->routers = VECTOR_Make((VECTOR_Key)GetIServerProxy, (VECTOR_Compare)CompareIServerProxy);

        endpoint->name  = name;

        endpoint->identity.handle = (uint32_t)INVALID_INDEX;

        endpoint->identity.token  = (uint32_t)INVALID_INDEX;   

        endpoint->identity.cookie = (uint32_t)INVALID_INDEX;

        endpoint->registerEP = RegisterRemoteEndpoint;

  • context = OpenLiteIpc() 打开本进程这一端的IPC通信通道,获取上下文,相当于拿到了开启通道一端大门的钥匙,要能够进行IPC通信,需要另一端的大门也打开才行。
  • boss:本EP的专用于IPC通信的线程的handle,还没创建线程,目前是NULL;
  • endpoint->routers:这里先创建一个空的向量,配置向量的key和compare函数。本进程内所有的service/feature中,符合条件的service/feature才能添加到这个向量中,成为这个routers Vector中的一个element,也就是内部的通信节点,这样才能对外部进程提供服务和接口。在具体的IPC通信时,会通过下面的endpoint->identity.token来确认是哪个element提供服务。
  • name: 就是"ipc client"字符串,作用不大;
  • endpoint->identity.handle:是本EP向管理者注册自己后,管理者为本EP返回的一个handle,非常重要,目前还没有向管理者注册自己,所以是INVALID_INDEX,这个INVALID_INDEX是特定的值[5],为什么是5,后面再讲。
  • endpoint->identity.token:具体的IPC通信中才会用到,用来标记本次IPC通信中,需要endpoint->routers 向量中的具体哪个element提供服务或接口。
  • endpoint->registerEP:是函数指针,指向本EP向管理者注册自己的函数,因为是"ipc client" EP,所以注册函数是RegisterRemoteEndpoint(),要是"samgr" EP,注册函数就会是RegisterSamgrEndpoint()。


  • [3-2] SAMGR_AddRouter(g_remoteRegister.endpoint, &saName, identity, iUnknown)
在上一步创建好的EP内,把“符合条件”的service和/或feature作为本EP内部的一个通信节点(router)添加到endpoint->routers向量里面去,进行管理。

“符合条件”的条件,有几个,关键的两个如下:
  • 条件1:IUnknown *proxy 不能为NULL
service/feature对外提供的接口不能为空,为空也就意味着外部进程没法使用service/feature提供的服务了,也就没必要加到endpoint->routers向量里去了。

这个条件可以过滤掉很多service,比如Broadcast服务,它在Init时,只通过RegisterService()注册了服务,并没有注册defaultApi,并且它自己也是有feature的,所以它的serviceImpl->defaultApi是NULL:



而向WMS服务(IMS服务也如此),在Init时注册了default feature API,同时它自己也没有feature,所以它的serviceImpl->defaultApi不是NULL,而是指向了WMSService 结构体内部的IUnknown接口对象,以此向外部进程提供功能。



  • 条件2:SERVER_PROXY_VER 要匹配 0x80
上一个条件的defaultApi不为NULL,指向了service/feature结构体内部的IUnknown接口对象,而这个接口对象的版本ver,要满足条件(匹配SERVER_PROXY_VER,即0x80),才能将其添加到endpoint->routers向量里,去对外部进程提供服务。



都满足条件了,还要在endpoint->routers向量里先查找一下,确认当前接口proxy是否已经在向量表里了,已经在了的话,就不能重复添加。

当前接口proxy没在向量表里,那就可以创建一个router对象,将提供proxy接口的service/feature、identity、serverProxy等相关信息配置好,将router对象(指针)添加到endpoint->routers向量里。

添加router成功后,会调用 Listen(endpoint):



第一句,boss 不为NULL 就return掉,意味着,第一个添加到endpoint->routers向量里的router添加成功后,就进来创建boss线程,专门用于本进程的对外IPC通信,后面再添加router成功时,因为boss线程已经在对外IPC通信了,所以不需要重复创建boss线程。

创建的boss线程,跑Receive()入口,具体做什么事情,我们稍后再讲。

[3-3] SAMGR_ProcPolicy(g_remoteRegister.endpoint, &saName, token)
能跑到这一步来,也是要首先满足两个条件:
1. 上一步的router成功添加到endpoint->routers向量里,拿到了有效的token,这个token就是router在向量的位置,也就是endpoint->routers->data[token]是一个指针,指向添加成功的router;
2. 本进程的g_remoteRegister.endpoint->running 标记要为TRUE,这意味着管理者那端的IPC通道也打开了,本进程EP已经完成了向管理者注册,拿到了本EP中SvcIdentity identity关键的handle,本进程可以开始对外提供服务了。

两个条件都满足后,这里就是要向管理者注册feature并且获取这个feature的访问权限策略信息,并保存在router的policyNum/policy 字段内。看上去这个函数的作用与RegisterRemoteFeatures()的作用差不多。

接下来,我们看一下boss线程的Receive()入口函数,都做了些什么事情。我把它分成4个阶段来理解:
[4-1] 第一阶段,向管理者知名EP注册本EP,获取本EP的身份信息中的handle。这步需要管理者g_server跑起来,知名EP打开IPC通道,这里才能通过IPC注册本EP,在知名EP还没跑起来之前,本进程EP的这一阶段会跑如下的循环:



registerEP是上面创建EP时配置的EP注册函数,对"ipc client" EP,注册函数是RegisterRemoteEndpoint()。

进入RegisterRemoteEndpoint()里面看一下,又是一个while循环,循环内会发送IPC信息,向知名EP samgr注册本EP,知名EP的信息直接硬编码写成:
SvcIdentity samgr = {SAMGR_HANDLE, SAMGR_TOKEN, SAMGR_COOKIE};  //{0, 0, 0}
这就是所谓的“知名”了。

这个内部循环,注册成功就拿到了SvcIdentity *identity中的handle,注册不成功(主要原因是知名EP还没开始工作),就会sleep(5s)再重新尝试。

内外两层循环合在一起,相当于在60s内尝试注册9次,正常情况下只要知名EP跑起来了,就肯定能注册成功的。

注册成功,或者一分钟内注册不成功,就会进入下面的第二阶段。

[4-2] 第二阶段,注册成功,就拿到了知名EP返回来的SvcIdentity identity.handle。
一分钟内注册不成功,直接就exit (-ret),意味着本进程要退出了,它的父进程(用户态根进程)Init应该就会收到SIGTERM或SIGCHLD信号,Init进程根据 /etc/init.cfg 的配置,来决定是重启单个进程,还是重启整个系统,后面的事情就另说了。

[4-3] 第三阶段,注册成功,就可以继续往下跑了,EP的状态endpoint->running = TRUE; 标记置起来。

因为知名EP也会跑Receive() 的这些流程,但知名EP不需要跑[4-3]的RegisterRemoteFeatures(endpoint) 这一步,所以通过[4-1]获取的endpoint->identity.handle来判断是否是知名EP的handle,是知名EP的话,就跳过,不是的话,就调用RegisterRemoteFeatures(endpoint)来把本EP的features(也就是本EP的routers向量中的所有element)全部注册到知名EP里去。

注册的过程也比较简单,就是遍历EP->routers向量,把router在向量中的位置序号填写到SvcIdentity identity.token,本EP的handle填写到SvcIdentity identity.handle,连同本router的其它相关信息一并,通过IPC消息发给知名EP,并且由知名EP返回注册成功和访问权限策略信息,再次填写回本EP对应的router里。

[4-4] 第四阶段,接下来就StartLoop(),本EP的boss线程进入监听IPC通信消息的状态,如果别的进程有IPC消息发送到本EP的handle,boss线程就可以监听到,然后调用Dispatch()函数来处理该消息。

到这里为止,系统服务的启动和注册就完成了,我们通过log来确认一遍上述过程
附件log是系统用户态进程启动到系统稳定的log,开始是shell/apphilogcat先启动,接着是bundle_daemon/sa_server/sensor_service这三个依赖关系相对简单的服务启动,它们的启动流程也会完全符合上面的几个步骤的,但我们还是接着前文,继续往下分析wms_server进程的启动。



上图是wms_server进程依赖的service/feature的Init,以及main函数的[5-1]这步。接着我们跳过一大段media_server的启动log。



上图是wms_server进程启动的[5-2]/[5-3]给service创建线程和消息队列,开始监听进程内部的多线程通信,这些都是前文解释过了的。



上图开始进入broadcast 服务的DEFAULT_Initialize()的流程,很明显可以看出对应着上面分析的四个步骤。

但是SAMGR_RegisterServiceApi()内的三个步骤,只跑了前两步,因为service和feature的SAMGR_AddRouter()这一步都是NG了,没有router添加成功,自然就不跑第三步了,也就是说本进程中,broadcast service和feature,不对外部进程提供服务和接口。跑完这里,我打印出了当前进程的g_remoteRegister(注意{}内的地址,不同进程的g_remoteRegister的地址是不一样的)全局变量的信息,见 DbgParse_g_remote{0x225922c8},可见还是和初始化状态一样的。



上图开始进入WMS服务的DEFAULT_Initialize()的流程,很明显可以看出对应着上面分析的前两个步骤,因为“RegFeatureApi(NO Feature)”,所以就没有后面两步了。

因为defaultApi不为NULL,并且QueryInterface的结果(版本匹配)也是OK的,所以SAMGR_AddRouter OK,第一个router被添加到了EP里面,所以开始Listen的流程。

为当前EP{0x2247cd00}创建专门用于对外IPC通信的boss监听线程,并开始执行Receive()入口函数的[4-1],开始两层循环尝试向知名EP注册本EP,但是由于知名EP还没有启动,所以会得到:
“[ERR][hm_liteipc] LiteIpcIoctl(IPC_SEND_RECV_MSG) ServiceManager not set!”
这个时候的EP状态如下:



有了一个router,但是handle还是 -1,需要等待,一直等到非常后面,如下图,才会继续执行[4-2]/[4-3],注册EP成功并拿到handle为16,然后执行[4-4]StartLoop:




上图是IMS服务的DEFAULT_Initialize()的流程,与WMS的类似,也可以看出因为没有feature,只跑了前两个步骤。因为本EP已经有boss线程在跑了,所以这里直接Listen boss线程就可以返回了。

此时的EP状态如下图,两个router可以对外提供服务,handle需要等待注册EP成功才会拿到 16 这个值。



接着就是下面的两步,
[wms.cpp] main[5-4]: GetInstance()->Run()
[wms.cpp] main[5-5]: while(1)
等EP拿到handle后,进程wms_server就可以顺利对外提供服务了。

系统服务启动和注册的log.rar
(9.38 KB, 下载次数: 0)


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友