[文章]Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往

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

前面只是简单说到client EP发送注册消息给samgr EP然后获取handle就完成了EP的注册过程,这里我们来看看具体都做了哪些事情。

实际上client EP与Server(samgr EP)的所有IPC过程(如clien EP向server注册Feature、查询Feature等等)也是类似的。

首先从client EP的RegisterRemoteEndpoint()开始(详细的IPC信息封装和序列化处理,这里先不深入解析):

SvcIdentity samgr = {SAMGR_HANDLE, SAMGR_TOKEN, SAMGR_COOKIE};  //{0, 0, 0}

    Transact(context, samgr, INVALID_INDEX, &req, &reply, LITEIPC_FLAG_DEFAULT, (uintptr_t *)&replyBuf);

这个Transact() 就是定义在liteipc_adapter.c中的SendRequest()函数,用来发送IPC请求,部分参数的意义如下:
  • context: IPC通信通道的上下文
  • samgr: IPC通信接收方的身份信息,handle标明哪个EP接收信息,token标明EP里的哪个router处理信息,这里知名EP的handle和token都是硬编码写死了的。
  • req: IPC通信中传送的命令和重要参数,经过序列化处理了,接收端反序列化处理即可解析出来。
  • reply:类似req,但是是信息接收端处理完消息后,反馈回来的应答信息,通过下面的identity->handle = IpcIoPopUint32(&reply); 看到能够从中解析出注册完成后返回来的handle。

再来看看samgr EP接收到这个IPC请求(注册EP)信息后如何处理。
Samgr EP的boss监控线程会在StartLoop循环的中收到发给SvcIdentity samgr = {0, 0, 0} 的IPC消息,通过Dispatch()函数来处理:



Dispatch()函数对IPC消息的处理,其实也算是很简单的,主要是根据IPC拿到的token,把需要处理这个IPC消息的router从Vector中取出来,首先确认router是否存在,不存在肯定就没法处理这个IPC消息了。router存在,就把相关信息填写到Request和Response里,再调用

SAMGR_SendSharedDirectRequest(&router->identity, &request, &resp, &ref, HandleIpc);

向router->identity 中的Qid队列发送direct request消息,处理完消息需要返回应答到resp里,消息指定要用HandleIpc() 函数来进行处理,相当于借Qid对应的那个service的线程,执行一下HandleIpc()函数。

实际处理IPC信息的是HandleIpc()函数:



最终,调用router->proxy->invoke()接口来处理消息,并返回应答。
实际上,所有EP的boss线程,对收到别的EP发过来的IPC消息,都是上面的处理过程,Dispatch()收到信息,转发信息,HandleIpc()处理消息,最终都是由消息接收者EP通过token指定的router->proxy->invoke()来处理。

对应samgr这个EP来说,这个router->proxy->invoke(),就是g_server.Invoke(),它们是怎么对应到一起的?

  1. static SamgrServer g_server =  //samgr server
  2. {
  3.     .GetName      = GetName,
  4.     .Initialize       = Initialize,
  5.     .GetTaskConfig  = GetTaskConfig,
  6.     .MessageHandle = MessageHandle,
  7.     SERVER_IPROXY_IMPL_BEGIN,
  8.     .Invoke        = Invoke,
  9.     IPROXY_END,
  10. };
复制代码

把这个g_server的接口部分展开:



它在通过 SAMGR_AddRouter(server->samgr, &saName, &server->identity, GET_IUNKNOWN(*server)); 添加router时,所用的第四个参数GET_IUNKNOWN(*server),就是获得g_server的 .iUnknown 字段的地址,以此地址作为SAMGR_AddRouter(......,IUnknown *proxy) 的proxy参数,然后:

IServerProxy *serverProxy = NULL;

    proxy->QueryInterface(proxy, SERVER_PROXY_VER, (void *)&serverProxy);  //0x80

在QueryInterface()时,实际执行 IUNKNOWN_QueryInterface(),会把 IUnknown 类型的*proxy(第一个参数),重新下转换成IUnknownEntry类型的指针*entry:

    IUnknownEntry *entry = GET_OBJECT(proxy, IUnknownEntry, proxy);

判断它的版本entry->ver是否符合要求,符合要求的话,它的引用entry->ref+1,然后返回proxy给第三个参数(void*) serverProxy,再经过类型转换到IServerProxy *serverProxy(也就是SamgrProxy的对象指针),然后:

router->proxy = serverProxy;   

这就对应起来了。从log中调用的Invoke函数的地址也可确认是同一个函数。



其他所有进程EP内的routers中的router,都以类似的方式将Invoke函数映射起来,也是这样方式调用的。

到了g_server.Invoke()函数内,就开始比较明朗了。g_server有一个函数指针数组g_functions[]:

  1. static ProcFunc g_functions[] = {
  2.     [RES_ENDPOINT] = ProcEndpoint,
  3.     [RES_FEATURE]  = ProcFeature,
  4.     [RES_SYSCAP]   = ProcSysCap,
  5. };
复制代码

g_server.Invoke()函数内通过对IPC过来的参数作为分类参数,以此选择对应的函数来分别做处理。

对EP注册类消息,调用ProcEndpoint()来处理:



对Feature类消息,调用ProcFeature()来处理,又分为注册和查询两种,通过参数分别调用ProcPutFeatur()和ProcGetFeature()来处理。

注册Feature消息的IPC发送端,在client EP启动和注册过程中,通过调用SAMGR_ProcPolicy()或者RegisterRemoteFeatures()来向samgr EP注册Feature。请去这两个函数去看代码应该就可以理解了。

查询Feature消息的IPC发送端,是在尝试使用别的进程/EP提供的服务/功能前,先向samgr EP查询并获取Feature接口,才能去使用。

注册和查询Feature,都需要涉及service/feature在g_server内的存储方式,我们先来看一下它的SAStore store字段。

SAStore 结构体及其相关的其他结构体,定义在sa_store.h 内,这是一个链表+向量的结构:

  1. struct SAStore {
  2.     int        saSize;
  3.     ListNode  *root;     //a list of services with their features
  4.     int16      mapSize;
  5.     int16      mapTop;
  6.     PidHandle *maps;    //a vector
  7. };
复制代码

ListNode *root; 是一个链表头部指针,链表的每一个节点是client EP注册进来的一个service,每个service节点,又包含一个子链表,子链表每个节点又是这个service提供的一个feature,没有提供feature的service,其子链表也会有一个名字为NULL,token为0的feature节点。

PidHandle *maps则是一个简化版本的向量,每一个element是一个指向PidHandle结构体的指针,每个PidHandle结构体则记录了向samgr EP注册过来的其他client EP的id信息:

  1. struct PidHandle {
  2.     pid_t pid;      //client EP 所在进程的 pid
  3.     uid_t uid;      //client EP 所在进程拥有者的 uid
  4.     uint32 handle;  //client EP 的boss线程的tid 或者 tid经过内核映射的编号
  5.     uint32 deadId;  
  6. };
复制代码



整个SAStore 结构体展开如上图,需要注意的是,client EP是先注册EP,在maps向量中记录了handle,然后才会注册Feature,往list中添加节点的时候,新节点的ServiceInfo中的handle,一定是与maps中对应的pid中的handle匹配的。而FeatureNode中的token字段,与这个Feature在client EP中的routers向量中的index也是匹配的。

ProcPutFeatur()函数的处理过程如下:



ProcGetFeature()函数的处理过程如下:



对于SysCap类的IPC消息,调用ProcSysCap()来处理,分为三类:添加一项SysCap、获取一项SysCap、获取所有SysCap。

在SYS_SERVICE_INIT(InitializeRegistry)阶段,就通过调用ParseSysCap()来读取和分析“/etc/system_capability.json”文件,将系统的Capabilitys,按格式添加到g_server的Vector sysCapabilitys中去了。sysCapabilitys的结构如下:



所以ProcSysCap()对三类操作,都按向量的操作方法,对sysCapabilitys操作即可。

当目前所有的client EP向g_server注册完EP/service/feature等等信息之后,g_server的完全展开信息如下:



系统启动之初通过init创建的3~11号线程中的3/4/8三个进程,不需要通过g_server的管理再向外提供服务,也可以看出这三个进程的特殊性。

List列出来的service/feature,是目前系统内不同进程之间能够相互访问的服务和特性,它们之间是如何互访的,我们下一节来详细分析。

回帖

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