前情回顾
在上一期博客中我们介绍了server和client EP的IPC来往流程,其中流程主要分为三个步骤:
- Dispatch()接收消息,然后转发消息
- HandleIpc()对消息进行处理
- 最后由消息接收者EP通过调用token指定的router->proxy->invoke()来处理
那么今天我们来简单介绍一下所谓的router->proxy->invoke()接口究竟是何物,并且以它为引子继续介绍其中的来往流程。
干货满满,可直接食用
震惊!router->proxy->invoke()的本质竟然是它!
我查阅了许多在鸿蒙开发领域的大佬的博客,最后苦思冥想,得出了一个结论:
由于router->proxy->invoke()接口主要存在于服务端和客户端之间,而对应我们的中介处——samgr而言,这个router->proxy->invoke(),就是g_server.Invoke()
没错,就是在很久之前的博客中我们提到的一个函数封装,具体详情可以查看博客:服务端那些事儿
static SamgrServer g_server = {
.GetName = GetName, //获得函数名
.Initialize = Initialize, //初始化
.GetTaskConfig = GetTaskConfig, //定义服务的任务配置
.MessageHandle = MessageHandle, //处理服务消息
SERVER_IPROXY_IMPL_BEGIN, //定义服务器代理类的默认初始化的开始。
//此宏用于开发服务器代理类,可以继承此宏以减少代码量并防止类定义不一致。
.Invoke = Invoke, //从客户端向IServerProxy发送IPC消息
IPROXY_END,
};
那么问题来了,这两者是如何对应到一起的呢?
在反复对比研究了几位华为工程师发表的博客后,在所有知识串起来的一刹那,我感觉整个人都达到了融会贯通,天人合一的境界!
我们先看下面这部分的初始化操作代码,它是g_server里最重要的部分:
static BOOL Initialize(Service *service, Identity identity) //初始化服务和身份的功能
{
SamgrServer *server = (SamgrServer *)service;
server->identity = identity;
SaName saName = {SAMGR_SERVICE, NULL};
SAMGR_AddRouter(server->samgr, &saName, &server->identity, GET_IUNKNOWN(*server));//添加路由器
return TRUE;
}
在初始化中有一个add router的操作,所以他的流程应该是这样的:
- g_server首先通过SAMGR_AddRouter函数添加router,在添加router时,我们要注意该函数所用的第四个参数,也就是GET_IUNKNOWN(*server),这个参数的功能就是用来获取g_server的 iUnknown 字段的地址,并且以此地址为该函数的proxy参数。
- 然后通过这个参数来实现查询接口的功能,也就是在QueryInterface()时,实际上执行的是 IUNKNOWN_QueryInterface(),结果就是会把 IUnknown 类型的proxy(第一个参数),重新下转换成IUnknownEntry类型的指针entry。
- 该指针的用途就是判断它的版本entry->ver是否符合要求,在符合要求的前提下,才能调用它的引用entry->ref+1,然后返回proxy给第三个参数(void*) serverProxy,再经过类型转换到IServerProxy *serverProxy。从而实现从router→proxy=serverProxy的过程。
于是乎,走了一遍流程之后我们发现,其中大部分的信息都对应起来了,在其他的进程中的EP内的router,都会以类似的方式将Invoke函数映射起来,同时也是以这种方式进行调用的。
文字的描述看起来似乎有些让人难以理解,所以下面我用一个较为简洁的流程图描述了上述过程:
搞清楚了Client、Server、Samgr中的相关对应函数后,我们也了解到了在g_server.Invoke()函数内是通过对IPC发送过来的参数作为分类参数,以此选择对应的函数来分别做处理。
探究!挖出Client与Server之间消息处理的幕后黑手
不知道大家是否还记得我曾经提到的几个Proc函数,没错,他们就是最后的黑老大!
在samgr_server.c文件中,有好几个Proc函数,他们分别对应EP中的不同功能:
- 对EP注册类消息,调用ProcEndpoint()来处理
- 对Feature类消息,调用ProcFeature()来处理,又分为注册和查询两种,通过参数分别调用ProcPutFeatur()和ProcGetFeature()来处理。
- 对于SysCap类的IPC消息,调用ProcSysCap()来处理,分为三类:添加一项SysCap、获取一项SysCap、获取所有SysCap。分别对应的三个函数为ProcAddSysCap()、ProcGetSysCap()、ProcGetAllSysCap()。
相关函数的功能和构建细节,可以查阅我之前的博客:
服务端的那些事儿(2)
在这里呢我大致介绍一下该机制对于Feature类消息的处理:
- 首先呢,在注册Feature消息的IPC发送端,在client EP启动和注册过程中,通过调用SAMGR_ProcPolicy()或者RegisterRemoteFeatures()来向samgr EP注册Feature
- 查询Feature消息的IPC发送端,是在尝试使用别的 进程/EP提供的服务/功能 前,先向samgr EP查询并获取Feature接口,才能去使用。
下期预告
不出意外的话,下一期的博客应该就是最后的总结,我将对我在系统框架中整理的模块内容进行一个梳理。
欲知后事如何,且听下回分解。