更多内容请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com继续上一篇文章《Hi3516的SAMGR--系统服务框架子系统-6-系统服务的启动》,继续从DEFAULT_Initialize(ServiceImpl*impl)函数的入口开始分析wms_server进程后续的启动步骤和注册EP的过程。从上一篇文章可以看出,wms_server进程启动了三个服务:Broadcast、WMS和IMS。Broadcast服务有一个特性:“Providerandsubscriber”,其他两个服务没有特性。每个服务都有自己的消息队列,都会收到InitRequest消息。通过HandleInitRequest()函数,服务将运行具有自身特性的DEFAULT_Initialize(serviceImpl)函数。在//foundation/distributedschedule/samgr_lite/samgr/source/service.cDEFAULT_Initialize()中,很明显可以分为以下四步:前两步针对service[4-1]impl->service->Initialize(impl->service,id)这一步会调用服务自身生命周期的Initialize()函数进行初始化。[4-2]SAMGR_RegisterServiceApi(serviceName,NULL,&id,impl->defaultApi)这一步轻量级系统执行空函数,没有实际动作;小系统执行remote_register.c中定义的函数,注意参数。对于服务的所有特征,for循环允许每个特征执行[4-3]和[4-4]两个步骤。如果服务没有feature,则不需要执行这两个步骤:[4-3]feature->feature->OnInitialize(feature->feature,impl->service,id)这一步会调用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()每个进程都有一个全局的RemoteRegisterg_remoteRegister,在这个进程的所有服务中,第一个运行在这里的服务会初始化这个g_remoteRegister,主要是创建一个互斥信号量,创建一个空的Vector客户端,并为此进程终端端点创建通信。以后service/feature再次进入这一步时,基本会退出,因为endpoint已经存在(在某些情况下,g_remoteRegister会被清除并重新生成,此处忽略)。让我们先专注于制作EP。//foundation/distributedschedule/samgr_lite/samgr_endpoint/source/endpoint.c所有进程都会通过SAMGR_CreateEndpoint("ipcclient",NULL)创建一个名为"ipcclient"的EP,只有manager会创建一个名为"samgr"的EPEP,这些事会晚一些讨论。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通信通道,获取context,相当于在通道的一端拿到了开门的钥匙。为了能够与IPC通信,您需要打开另一端的门。boss:本EP中专用于IPC通信的线程句柄。线程尚未创建,当前为NULL;endpoint->routers:这里先创建一个空vector,配置vector的key和compare函数。在这个进程的所有服务/特征中,符合条件的服务/特征可以加入到这个vector中,成为这个routersVector中的一个元素,即内部通信节点,对外提供服务和接口。在具体的IPC通信中,会通过下面的endpoint->identity.token来确认是哪个元素提供了服务。name:是“ipcclient”字符串,作用不大;endpoint->identity.handle:EP向管理器注册后,管理器为该EP返回的句柄。它非常重要,还没有发送给经理。自己注册,所以是INVALID_INDEX,这个INVALID_INDEX是一个具体的值[5],为什么是5,后面再说。endpoint->identity.token:只会在特定的IPC通信中使用。用于标记endpoint->routers向量中的哪个特定元素需要在本次IPC通信中提供服务或接口。endpoint->registerEP:是一个函数指针,指向这个EP向manager注册自己的函数,因为是“ipcclient”EP,所以注册函数是RegisterRemoteEndpoint(),如果是“samgr”EP,注册函数将是RegisterSamgrEndpoint()。[3-2]SAMGR_AddRouter(g_remoteRegister.endpoint,&saName,identity,iUnknown)将“合格”服务和/或功能作为EP内部的通信节点(路由器)添加到上一步创建的EP进入端点内部->用于管理的路由器向量。“资格”有几个条件,其中最关键的两个如下:条件一:IUnknown*proxycannotbeNULL服务/特性提供的接口不能为空,为空表示外部进程不能使用service/feature提供的服务,不需要添加到endpoint->routers向量中。这个条件可以过滤掉很多服务,比如Broadcast服务,它在Init时只通过RegisterService()注册了服务,并没有注册defaultApi,而且它也有特点,所以它的serviceImpl->defaultApi为NULL:而对于WMS服务(IMS服务同理),默认的特征API是在Init注册的,它本身没有特征,所以它的serviceImpl->defaultApi不为NULL,而是指向WMSService结构体内部的IUnknown接口对象,用这个为外部进程提供功能。条件2:SERVER_PROXY_VER必须匹配0x80。前面条件的defaultApi不为NULL,指向service/feature结构体内部的IUnknown接口对象,这个接口对象的版本ver必须满足条件(匹配SERVER_PROXY_VER,即0x80)才能使用它是添加到endpoint->routers向量以向外部进程提供服务。如果所有条件都满足,则需要先在endpoint->routers向量中查找,确认当前接口代理是否已经在向量表中。如果已经存在,则不能重复添加。当前接口代理不在vector表中,那么可以创建一个router对象,配置提供代理接口的service/feature,identity,serverProxy等相关信息,将router对象(指针)添加到endpoint->路由器矢量。router添加成功后,会调用Listen(endpoint):第一句,如果boss不为NULL,则返回,意思是endpoint->routersvector中添加的第一个router添加成功后,它将进来并创建一个老板线程。专门用于本进程对外的IPC通信。后面添加router成功的时候,因为boss线程已经在和外部IPC通信了,所以不需要重复创建boss线程。创建的boss线程运行的是Receive()入口,具体是干什么的,后面再说。[3-3]SAMGR_ProcPolicy(g_remoteRegister.endpoint,&saName,token)可以走到这一步,但是也必须先满足两个条件:1.上一步中的router成功添加到endpoint->routersvector中,并且avalidtoken,这个token是router在vector中的位置,即endpoint->routers->data[token]是指向添加成功的router的指针;2、这个进程的g_remoteRegister.endpoint->runningflag必须为TRUE,也就是说manager端的IPC通道也打开了。这个进程EP已经完成了向manager的注册,并且在这个EP中得到了SvcIdentity身份的keyhandle。这个进程就可以开始对外提供服务了。两个条件都满足后,这里就是向manager注册feature,获取这个feature的访问权限策略信息,保存在路由器的policyNum/policy字段中。看起来这个函数与RegisterRemoteFeatures()做同样的事情。接下来我们看一下boss线程的Receive()入口函数及其作用。我分为4个阶段来理解:[4-1]第一阶段,将这个EP注册到manager的熟知EP中,在这个EP的身份信息中获取handle。这一步需要运行管理器g_server,知名EP开通IPC通道,EP可以通过IPC进行注册。在众所周知的EP运行之前,这一阶段的EP流程会运行如下循环:registerEP是上面创建EP时配置的EP注册函数,对于“ipcclient”EP,注册函数是RegisterRemoteEndpoint()。转到RegisterRemoteEndpoint()并查看。这是另一个while循环。循环中会发送IPC信息,将这个EP注册到知名EPsamgr中。知名EP的信息直接硬编码为:SvcIdentitysamgr={SAMGR_HANDLE,SAMGR_TOKEN,SAMGR_COOKIE};//{0,0,0}这就是所谓的“知名”。在这个内部循环中,如果注册成功,就会得到SvcIdentity*identity中的句柄。如果注册不成功(主要原因是知名EP还没有开始工作),会sleep(5s)重试。内循环和外循环的组合相当于在60秒内尝试注册9次。一般情况下,只要知名EP运行,注册就会成功。若注册成功,或一分钟内注册不成功,则进入下方第二阶段。[4-2]第二阶段,注册成功,得到知名EP返回的SvcIdentityidentity.handle。如果一分钟内注册不成功,直接退出(-ret)表示进程即将退出,其父进程(用户态根进程)init应该收到SIGTERM或SIGCHLD信号。Init进程根据/etc/init.cfg的配置来决定是重启单个进程还是重启整个系统,后者将在别处讨论。[4-3]第三阶段,如果注册成功,可以继续往下跑,EP状态endpoint->running=TRUE;标志已设置。因为知名EP也会运行Receive()的这些过程,但是知名EP不需要运行[4-3]的RegisterRemoteFeatures(endpoint)步骤,所以通过endpoint->identity来判断[4-1]中得到的.handle是一个知名EP的handle。如果是知名EP,请跳过。如果不是,则调用RegisterRemoteFeatures(endpoint)将这个EP的所有特征(即这个EP的routers向量中的所有元素)注册到众所周知的EP。.注册过程也比较简单,就是遍历EP->routers向量,将router在向量中的位置编号填入SvcIdentityidentity.token,将这个EP的handle填入SvcIdentityidentity.handle,一起和本路由器的其他相关信息,通过IPC消息发送给知名EP,知名EP返回注册成功和访问权限策略信息,并重新填写本EP对应的路由器。[4-4]第四阶段,下一步是StartLoop()。这个EP的boss线程进入监听IPC通信消息状态。如果其他进程有IPC消息发送到这个EP的句柄,boss线程就可以监听到。然后调用Dispatch()函数来处理消息。至此,系统服务的启动和注册就完成了,我们通过日志确认以上过程。附件日志是系统用户态进程启动到系统稳定的日志。首先是shell/apphilogcat先启动,然后是bundle_daemon/sa_server/sensor_service,这三个依赖关系比较简单的服务,它们的启动过程完全按照上面的几个步骤,不过我们还是按照上一篇继续分析wms_server进程的启动。上图是wms_server进程所依赖的service/feature的Init,以及main函数的[5-1]步骤。然后我们跳过一大段media_server启动日志。上图为[5-2]/[5-3]由wms_server进程启动,为服务创建线程和消息队列,并开始监听进程内部的多线程通信,上面已经说明.上图开始进入广播服务的DEFAULT_Initialize()流程,很明显对应上面分析的四个步骤。但是SAMGR_RegisterServiceApi()中的三步只运行前两步,因为service和feature的SAMGR_AddRouter()这一步是NG的,如果router没有添加成功,自然不会运行第三步,即也就是说,在这个进程中,broadcastservice和feature不对外进程提供服务和接口。运行到这里后,我打印出了当前进程的g_remoteRegister的全局变量信息(注意{}中的地址,不同进程的g_remoteRegister地址是不一样的),看DbgParse_g_remote{0x225922c8},可以看出还是与初始化状态相同。上图开始进入WMS服务的DEFAULT_Initialize()流程。很明显,它对应于上面分析的前两步。因为“RegFeatureApi(NOFeature)”,所以没有后续两步。因为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)ServiceManagernotset!”此时EP状态如下:有router,但是handle还是-1,需要等到最后,如下图,会继续执行[4-2]/[4-3],注册EP成功,得到句柄为16,然后执行[4-4]StartLoop:上图是IMS服务的DEFAULT_Initialize()过程,和WMS类似,也可以看出,因为有没有特征,只运行前两个步骤。因为这张EP已经有boss线程在运行,所以在这里听boss线程可以直接返回。此时的EP状态如下图所示。两个router都可以对外提供服务,handle需要等待EP注册成功才能得到16的值。然后就是下面两步,[wms.cpp]main[5-4]:GetInstance()->Run()[wms.cpp]main[5-5]:while(1)EP拿到句柄后,进程wms_server就可以顺利对外提供服务了。更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区