Hi3516的SAMGR--系統(tǒng)服務(wù)框架子系統(tǒng)-11-Client與Client的IPC來往
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
進程A中的線程Aa(也就是service Aa)想要調(diào)用某個service/feature的接口,這個接口可能是進程A自己的線程Ab(也就是service Ab)提供的,也可能是進程B的線程Bc(也就是service Bc)提供的。
如果是同進程的不同服務(wù)提供的接口,那Aa只需要向自己進程的SamgrLiteImpl g_samgrImpl查詢service/feature名字就可以拿到對應(yīng)的IUnknown *iUnknown接口,因為是同進程內(nèi)的相同虛擬地址空間,所以可以直接跨線程調(diào)用。如《系統(tǒng)服務(wù)框架子系統(tǒng)-4-面向服務(wù)架構(gòu)的實現(xiàn)》中的測試程序所做的那樣。
也就是通過調(diào)用SAMGR_GetInstance()->GetDefaultFeatureApi(SERVICE_NAME) 或者SAMGR_GetInstance()->FeatureApi(SERVICE_NAME,F(xiàn)EATURE_NAME)來查詢接口。
如果Aa想調(diào)用的service/feature接口是B進程提供的,那首先這個service,肯定在Aa所在進程的SamgrLiteImpl g_samgrImpl里面是找不到記錄的,也就是serviceImpl = NULL:

所以,跨進程的服務(wù)/特性的調(diào)用,入口就在上面的SAMGR_FindServiceApi(service, feature)。
結(jié)合我們的log,搜索一下“%%%%%%%%%%%”這樣一個字符串,這是我標記的一處入口(實際上搜索“SAMGR_FindServiceApi”也差不多)。
看一下log:
- {[bundle_daemon_client]} Initialize: GetDefaultFeatureApi(bundle_daemon)
這是“bundle_daemon_client”在Initialize的時候,向自己所在進程的SamgrLiteImpl g_samgrImpl查詢名字為“bundle_daemon”的服務(wù)的DefaultFeatureApi。
通過前后的log,我們可以知道bundle_daemon_client所在進程的信息“pid[ 5]/uid[ 7]/(*)handle[61](*)”,5號進程是“foundation”,而“bundle_daemon”服務(wù)是6號進程提供的。
所以會通過SAMGR_FindServiceApi(S[bundle_daemon],F[(null)])來查詢“bundle_daemon”服務(wù)提供的接口。
SAMGR_FindServiceApi()及其輔助函數(shù),涉及到了g_remoteRegister.clients 這個字段,我們先來理解一下。
這是一個向量,在本進程的 g_remoteRegister 初始化的時候,也對這個向量做了配置:
- g_remoteRegister.clients = VECTOR_Make((VECTOR_Key)SAMGR_GetSAName, (VECTOR_Compare)SAMGR_CompareSAName);
向量的element:data[x]是一個IUnknown *proxy類型的指針,指向的是一個客戶端代理的接口entry.iUnknown,通過這個指針,可以轉(zhuǎn)換回IClientProxy類對象、IClientEntry類對象、IDefaultClient類對象,再通過IClientProxy類對象指針去訪問后面的Invoke函數(shù),或者通過IDefaultClient類對象指針去訪問上面的IClientHeader header字段內(nèi)的key、target等字段。
向量中每一個element,記錄了本進程EP調(diào)用過的別的進程提供的服務(wù)接口相關(guān)的重要身份信息如名字、handle、token等,這樣下次再調(diào)用的時候,直接在這里查詢就能獲得這些信息,不需要再次通過IPC去向samgr EP查詢了,它的展開圖如下:

SAMGR_FindServiceApi()的流程如下:


我們接著上面的log往下看一下,

這是第一次bundle_daemon_client 向samgr EP查詢FeatureAPI,但是返回的 handle 是 -1,從接下來的DbgParse_g_server的信息來看,這個時候提供bundle_daemon服務(wù)的6號進程,還沒有向samge EP注冊EP和Feature,所以samgr EP自己也查不到。
再看第二次bundle_daemon_client 向samgr EP查詢FeatureAPI,這回查到了,handle是38,token是0,也就是說,SAMGR_CreateIProxy()的第一步,拿到了SvcIdentity identity= {38, 0, 0},跟著接下來的流程跑,就會得到一個完整有效的IDefaultClient對象,把這個對象中的 .iUnknown 地址先保存到g_remoteRegister.clients 向量中去,然后通過這個.iUnknown 地址,就可以調(diào)用客戶端代理的Invoke()接口,從而再通過IPC去遠程調(diào)用bundle_daemon服務(wù)了。

Privider: Pid[6]/Uid[8]/handle[38]:bundle_daemon -> (null)Consumer: Pid[5]/Uid[7]/Tid[53]
從這兩句log可以看出,6號進程的bundle_daemon 服務(wù)是provider,查詢并使用該服務(wù)的5號進程是consumer,這也是面向服務(wù)的架構(gòu)的一種實現(xiàn)。
我們再看另外一個更明顯的跨進程調(diào)用服務(wù)的例子:第一個應(yīng)用進程launcher的孵化啟動。
Log搜索“AppAppAppApp”,這是5號進程通過app_manager準備調(diào)用7號進程提供的“appspawn”服務(wù)來孵化launcher應(yīng)用,它拿到了“appspawn”的handle 74、token 0和.iUnknown地址,并把該地址以及相關(guān)的重要信息添加到自己的clients向量中。


接下來app_spawn_client調(diào)用上面的Invoke{0x249cc84c},這個Invoke{0x249cc84c}就是ProxyInvoke()函數(shù),通過它發(fā)送IPC消息給IpcMsg Rceiver::handle[74],token[0],讓它的EP收到和處理該消息,這就到了上一小節(jié)提到的Dispatch()和HandleIpc()對IPC消息的處理流程了。

經(jīng)過消息的轉(zhuǎn)發(fā)和處理,最后是appspawn_service的Invoke()函數(shù)開始孵化launcher應(yīng)用進程,桌面應(yīng)用開始提供服務(wù)。
跨進程的服務(wù)接口調(diào)用到此結(jié)束。
剛好剛才去瀏覽標準系統(tǒng)的 //base/startup/appspawn_standard/目錄,看了一下README_zh,看到下面這張圖就貼過來了,除了通信方式不一樣之外,大體流程是一樣的。

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)