Hi3516的SAMGR--系統(tǒng)服務(wù)框架子系統(tǒng)-client EP的注冊(cè)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
我們接著前文《Hi3516的SAMGR--系統(tǒng)服務(wù)框架子系統(tǒng)-6-系統(tǒng)服務(wù)的啟動(dòng)》,繼續(xù)分析wms_server進(jìn)程后繼的啟動(dòng)步驟和注冊(cè)EP的流程,從DEFAULT_Initialize(ServiceImpl *impl) 函數(shù)入口開始。
從前文可以知道,wms_server進(jìn)程啟動(dòng)了三個(gè)service:Broadcast、WMS和IMS,Broadcast服務(wù)有一個(gè)feature:“Provider and subscriber”,另外兩個(gè)服務(wù),沒有feature。每個(gè)service都有自己的消息隊(duì)列,都會(huì)收到InitRequest消息,通過HandleInitRequest()函數(shù),service會(huì)帶著自己的feature跑一遍DEFAULT_Initialize(serviceImpl)函數(shù)。
- //foundation/distributedschedule/samgr_lite/samgr/source/service.c
DEFAULT_Initialize()內(nèi),很明顯可以分為以下四步的:
針對(duì)service跑前兩步
- [4-1] impl->service->Initialize(impl->service, id)
這一步會(huì)調(diào)用service自己生命周期的 Initialize() 函數(shù)來做初始化。
- [4-2] SAMGR_RegisterServiceApi(serviceName, NULL, &id, impl->defaultApi)
這一步輕量系統(tǒng)執(zhí)行空函數(shù),沒有實(shí)際動(dòng)作;小型系統(tǒng)執(zhí)行remote_register.c 中定義的函數(shù),注意參數(shù)。
針對(duì)service所有的feature,for循環(huán)讓每個(gè)feature都執(zhí)行一遍[4-3]和[4-4]兩步,service沒有feature,就不用執(zhí)行這兩步:
- [4-3] feature->feature->OnInitialize(feature->feature, impl->service, id)
這一步會(huì)調(diào)用feature自己生命周期的 OnInitialize() 函數(shù)來做初始化。
- [4-4] SAMGR_RegisterServiceApi(serviceName, featureName, &id, feature->iUnknown)
這一步調(diào)用的API與第2步調(diào)用的是同一個(gè)API,但是注意參數(shù)的變化,特別是第四個(gè)參數(shù)。
- //foundation/distributedschedule/samgr_lite/samgr_client/source/remote_register.c
我們?cè)敿?xì)看一下SAMGR_RegisterServiceApi()的工作,它內(nèi)部,也很明顯可以分為以下三步的:
- [3-1] InitializeRegistry()
每一個(gè)進(jìn)程,都有一個(gè)全局的RemoteRegister g_remoteRegister,本進(jìn)程的所有services中,第一個(gè)跑到這里的service會(huì)去初始化這個(gè) g_remoteRegister,主要是創(chuàng)建互斥信號(hào)量、創(chuàng)建一個(gè)空的Vector clients、創(chuàng)建本進(jìn)程的通信終端endpoint。以后的service/feature再跑進(jìn)這一步時(shí),基本上都會(huì)因?yàn)橐呀?jīng)存在endpoint了而就此退出(特定條件下會(huì)清空本 g_remoteRegister,重新生成,這里先不管)。
我們先把關(guān)注的重點(diǎn)放在創(chuàng)建 EP上。
- //foundation/distributedschedule/samgr_lite/samgr_endpoint/source/endpoint.c
所有進(jìn)程都會(huì)通過SAMGR_CreateEndpoint("ipc client", NULL) 創(chuàng)建一個(gè)名為"ipc client"的EP,只有管理者會(huì)創(chuàng)建名為"samgr"的EP,這個(gè)后面講。
SAMGR_CreateEndpoint()內(nèi)會(huì)初始化如下一些參數(shù)(沒列出來的先略去):
- 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() 打開本進(jìn)程這一端的IPC通信通道,獲取上下文,相當(dāng)于拿到了開啟通道一端大門的鑰匙,要能夠進(jìn)行IPC通信,需要另一端的大門也打開才行。
boss:本EP的專用于IPC通信的線程的handle,還沒創(chuàng)建線程,目前是NULL;
endpoint->routers:這里先創(chuàng)建一個(gè)空的向量,配置向量的key和compare函數(shù)。本進(jìn)程內(nèi)所有的service/feature中,符合條件的service/feature才能添加到這個(gè)向量中,成為這個(gè)routers Vector中的一個(gè)element,也就是內(nèi)部的通信節(jié)點(diǎn),這樣才能對(duì)外部進(jìn)程提供服務(wù)和接口。在具體的IPC通信時(shí),會(huì)通過下面的endpoint->identity.token來確認(rèn)是哪個(gè)element提供服務(wù)。
name: 就是"ipc client"字符串,作用不大;
endpoint->identity.handle:是本EP向管理者注冊(cè)自己后,管理者為本EP返回的一個(gè)handle,非常重要,目前還沒有向管理者注冊(cè)自己,所以是INVALID_INDEX,這個(gè)INVALID_INDEX是特定的值[5],為什么是5,后面再講。
endpoint->identity.token:具體的IPC通信中才會(huì)用到,用來標(biāo)記本次IPC通信中,需要endpoint->routers 向量中的具體哪個(gè)element提供服務(wù)或接口。
endpoint->registerEP:是函數(shù)指針,指向本EP向管理者注冊(cè)自己的函數(shù),因?yàn)槭?quot;ipc client" EP,所以注冊(cè)函數(shù)是RegisterRemoteEndpoint(),要是"samgr" EP,注冊(cè)函數(shù)就會(huì)是RegisterSamgrEndpoint()。
- [3-2] SAMGR_AddRouter(g_remoteRegister.endpoint, &saName, identity, iUnknown)
在上一步創(chuàng)建好的EP內(nèi),把“符合條件”的service和/或feature作為本EP內(nèi)部的一個(gè)通信節(jié)點(diǎn)(router)添加到endpoint->routers向量里面去,進(jìn)行管理。
“符合條件”的條件,有幾個(gè),關(guān)鍵的兩個(gè)如下:
條件1:IUnknown *proxy 不能為NULL
service/feature對(duì)外提供的接口不能為空,為空也就意味著外部進(jìn)程沒法使用service/feature提供的服務(wù)了,也就沒必要加到endpoint->routers向量里去了。
這個(gè)條件可以過濾掉很多service,比如Broadcast服務(wù),它在Init時(shí),只通過RegisterService()注冊(cè)了服務(wù),并沒有注冊(cè)defaultApi,并且它自己也是有feature的,所以它的serviceImpl->defaultApi是NULL:

而向WMS服務(wù)(IMS服務(wù)也如此),在Init時(shí)注冊(cè)了default feature API,同時(shí)它自己也沒有feature,所以它的serviceImpl->defaultApi不是NULL,而是指向了WMSService 結(jié)構(gòu)體內(nèi)部的IUnknown接口對(duì)象,以此向外部進(jìn)程提供功能。

條件2:SERVER_PROXY_VER 要匹配 0x80
上一個(gè)條件的defaultApi不為NULL,指向了service/feature結(jié)構(gòu)體內(nèi)部的IUnknown接口對(duì)象,而這個(gè)接口對(duì)象的版本ver,要滿足條件(匹配SERVER_PROXY_VER,即0x80),才能將其添加到endpoint->routers向量里,去對(duì)外部進(jìn)程提供服務(wù)。

都滿足條件了,還要在endpoint->routers向量里先查找一下,確認(rèn)當(dāng)前接口proxy是否已經(jīng)在向量表里了,已經(jīng)在了的話,就不能重復(fù)添加。
當(dāng)前接口proxy沒在向量表里,那就可以創(chuàng)建一個(gè)router對(duì)象,將提供proxy接口的service/feature、identity、serverProxy等相關(guān)信息配置好,將router對(duì)象(指針)添加到endpoint->routers向量里。
添加router成功后,會(huì)調(diào)用 Listen(endpoint):

第一句,boss 不為NULL 就return掉,意味著,第一個(gè)添加到endpoint->routers向量里的router添加成功后,就進(jìn)來創(chuàng)建boss線程,專門用于本進(jìn)程的對(duì)外IPC通信,后面再添加router成功時(shí),因?yàn)閎oss線程已經(jīng)在對(duì)外IPC通信了,所以不需要重復(fù)創(chuàng)建boss線程。
創(chuàng)建的boss線程,跑Receive()入口,具體做什么事情,我們稍后再講。
- [3-3] SAMGR_ProcPolicy(g_remoteRegister.endpoint, &saName, token)
能跑到這一步來,也是要首先滿足兩個(gè)條件:
1. 上一步的router成功添加到endpoint->routers向量里,拿到了有效的token,這個(gè)token就是router在向量的位置,也就是endpoint->routers->data[token]是一個(gè)指針,指向添加成功的router;
2. 本進(jìn)程的g_remoteRegister.endpoint->running 標(biāo)記要為TRUE,這意味著管理者那端的IPC通道也打開了,本進(jìn)程EP已經(jīng)完成了向管理者注冊(cè),拿到了本EP中SvcIdentity identity關(guān)鍵的handle,本進(jìn)程可以開始對(duì)外提供服務(wù)了。
兩個(gè)條件都滿足后,這里就是要向管理者注冊(cè)feature并且獲取這個(gè)feature的訪問權(quán)限策略信息,并保存在router的policyNum/policy 字段內(nèi)。看上去這個(gè)函數(shù)的作用與RegisterRemoteFeatures()的作用差不多。
接下來,我們看一下boss線程的Receive()入口函數(shù),都做了些什么事情。我把它分成4個(gè)階段來理解:
[4-1] 第一階段,向管理者知名EP注冊(cè)本EP,獲取本EP的身份信息中的handle。這步需要管理者g_server跑起來,知名EP打開IPC通道,這里才能通過IPC注冊(cè)本EP,在知名EP還沒跑起來之前,本進(jìn)程EP的這一階段會(huì)跑如下的循環(huán):

registerEP是上面創(chuàng)建EP時(shí)配置的EP注冊(cè)函數(shù),對(duì)"ipc client" EP,注冊(cè)函數(shù)是RegisterRemoteEndpoint()。
進(jìn)入RegisterRemoteEndpoint()里面看一下,又是一個(gè)while循環(huán),循環(huán)內(nèi)會(huì)發(fā)送IPC信息,向知名EP samgr注冊(cè)本EP,知名EP的信息直接硬編碼寫成:
- SvcIdentity samgr = {SAMGR_HANDLE, SAMGR_TOKEN, SAMGR_COOKIE}; //{0, 0, 0}
這就是所謂的“知名”了。
這個(gè)內(nèi)部循環(huán),注冊(cè)成功就拿到了SvcIdentity *identity中的handle,注冊(cè)不成功(主要原因是知名EP還沒開始工作),就會(huì)sleep(5s)再重新嘗試。
內(nèi)外兩層循環(huán)合在一起,相當(dāng)于在60s內(nèi)嘗試注冊(cè)9次,正常情況下只要知名EP跑起來了,就肯定能注冊(cè)成功的。
注冊(cè)成功,或者一分鐘內(nèi)注冊(cè)不成功,就會(huì)進(jìn)入下面的第二階段。
[4-2] 第二階段,注冊(cè)成功,就拿到了知名EP返回來的SvcIdentity identity.handle。
一分鐘內(nèi)注冊(cè)不成功,直接就exit (-ret),意味著本進(jìn)程要退出了,它的父進(jìn)程(用戶態(tài)根進(jìn)程)Init應(yīng)該就會(huì)收到SIGTERM或SIGCHLD信號(hào),Init進(jìn)程根據(jù) /etc/init.cfg 的配置,來決定是重啟單個(gè)進(jìn)程,還是重啟整個(gè)系統(tǒng),后面的事情就另說了。
[4-3] 第三階段,注冊(cè)成功,就可以繼續(xù)往下跑了,EP的狀態(tài)endpoint->running = TRUE; 標(biāo)記置起來。
因?yàn)橹鸈P也會(huì)跑Receive() 的這些流程,但知名EP不需要跑[4-3]的RegisterRemoteFeatures(endpoint) 這一步,所以通過[4-1]獲取的endpoint->identity.handle來判斷是否是知名EP的handle,是知名EP的話,就跳過,不是的話,就調(diào)用RegisterRemoteFeatures(endpoint)來把本EP的features(也就是本EP的routers向量中的所有element)全部注冊(cè)到知名EP里去。
注冊(cè)的過程也比較簡(jiǎn)單,就是遍歷EP->routers向量,把router在向量中的位置序號(hào)填寫到SvcIdentity identity.token,本EP的handle填寫到SvcIdentity identity.handle,連同本router的其它相關(guān)信息一并,通過IPC消息發(fā)給知名EP,并且由知名EP返回注冊(cè)成功和訪問權(quán)限策略信息,再次填寫回本EP對(duì)應(yīng)的router里。
[4-4] 第四階段,接下來就StartLoop(),本EP的boss線程進(jìn)入監(jiān)聽I(yíng)PC通信消息的狀態(tài),如果別的進(jìn)程有IPC消息發(fā)送到本EP的handle,boss線程就可以監(jiān)聽到,然后調(diào)用Dispatch()函數(shù)來處理該消息。
到這里為止,系統(tǒng)服務(wù)的啟動(dòng)和注冊(cè)就完成了,我們通過log來確認(rèn)一遍上述過程。
附件log是系統(tǒng)用戶態(tài)進(jìn)程啟動(dòng)到系統(tǒng)穩(wěn)定的log,開始是shell/apphilogcat先啟動(dòng),接著是bundle_daemon/sa_server/sensor_service這三個(gè)依賴關(guān)系相對(duì)簡(jiǎn)單的服務(wù)啟動(dòng),它們的啟動(dòng)流程也會(huì)完全符合上面的幾個(gè)步驟的,但我們還是接著前文,繼續(xù)往下分析wms_server進(jìn)程的啟動(dòng)。

上圖是wms_server進(jìn)程依賴的service/feature的Init,以及main函數(shù)的[5-1]這步。接著我們跳過一大段media_server的啟動(dòng)log。

上圖是wms_server進(jìn)程啟動(dòng)的[5-2]/[5-3]給service創(chuàng)建線程和消息隊(duì)列,開始監(jiān)聽進(jìn)程內(nèi)部的多線程通信,這些都是前文解釋過了的。

上圖開始進(jìn)入broadcast 服務(wù)的DEFAULT_Initialize()的流程,很明顯可以看出對(duì)應(yīng)著上面分析的四個(gè)步驟。
但是SAMGR_RegisterServiceApi()內(nèi)的三個(gè)步驟,只跑了前兩步,因?yàn)閟ervice和feature的SAMGR_AddRouter()這一步都是NG了,沒有router添加成功,自然就不跑第三步了,也就是說本進(jìn)程中,broadcast service和feature,不對(duì)外部進(jìn)程提供服務(wù)和接口。跑完這里,我打印出了當(dāng)前進(jìn)程的g_remoteRegister(注意{}內(nèi)的地址,不同進(jìn)程的g_remoteRegister的地址是不一樣的)全局變量的信息,見 DbgParse_g_remote{0x225922c8},可見還是和初始化狀態(tài)一樣的。

上圖開始進(jìn)入WMS服務(wù)的DEFAULT_Initialize()的流程,很明顯可以看出對(duì)應(yīng)著上面分析的前兩個(gè)步驟,因?yàn)?ldquo;RegFeatureApi(NO Feature)”,所以就沒有后面兩步了。
因?yàn)閐efaultApi不為NULL,并且QueryInterface的結(jié)果(版本匹配)也是OK的,所以SAMGR_AddRouter OK,第一個(gè)router被添加到了EP里面,所以開始Listen的流程。
為當(dāng)前EP{0x2247cd00}創(chuàng)建專門用于對(duì)外IPC通信的boss監(jiān)聽線程,并開始執(zhí)行Receive()入口函數(shù)的[4-1],開始兩層循環(huán)嘗試向知名EP注冊(cè)本EP,但是由于知名EP還沒有啟動(dòng),所以會(huì)得到:
- “[ERR][hm_liteipc] LiteIpcIoctl(IPC_SEND_RECV_MSG) ServiceManager not set!”
這個(gè)時(shí)候的EP狀態(tài)如下:

有了一個(gè)router,但是handle還是 -1,需要等待,一直等到非常后面,如下圖,才會(huì)繼續(xù)執(zhí)行[4-2]/[4-3],注冊(cè)EP成功并拿到handle為16,然后執(zhí)行[4-4]StartLoop:


上圖是IMS服務(wù)的DEFAULT_Initialize()的流程,與WMS的類似,也可以看出因?yàn)闆]有feature,只跑了前兩個(gè)步驟。因?yàn)楸綞P已經(jīng)有boss線程在跑了,所以這里直接Listen boss線程就可以返回了。
此時(shí)的EP狀態(tài)如下圖,兩個(gè)router可以對(duì)外提供服務(wù),handle需要等待注冊(cè)EP成功才會(huì)拿到 16 這個(gè)值。

接著就是下面的兩步,
- [wms.cpp] main[5-4]: GetInstance()->Run()
- [wms.cpp] main[5-5]: while(1)
等EP拿到handle后,進(jìn)程wms_server就可以順利對(duì)外提供服務(wù)了。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)





















