Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往 原创 精华

liangkz_梁开祝
发布于 2021-7-2 17:50
浏览
2收藏

Hi3516的SAMGR--系统服务框架子系统-10

Client与Server的IPC来往

liangkz 2021.07.02

 

前面只是简单说到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()函数来处理:

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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()函数:

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

最终,调用router->proxy->invoke()接口来处理消息,并返回应答。

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

 

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

static SamgrServer g_server =  //samgr server
{
    .GetName      = GetName,
    .Initialize       = Initialize,
    .GetTaskConfig  = GetTaskConfig,
    .MessageHandle = MessageHandle,
    SERVER_IPROXY_IMPL_BEGIN,
    .Invoke        = Invoke,
    IPROXY_END,
};

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

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

它在通过 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函数的地址也可确认是同一个函数。

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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

 

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

static ProcFunc g_functions[] = {
    [RES_ENDPOINT] = ProcEndpoint,
    [RES_FEATURE]  = ProcFeature,
    [RES_SYSCAP]   = ProcSysCap,
};

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

 

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

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

对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 内,这是一个链表+向量的结构:

struct SAStore {
    int        saSize;
    ListNode  *root;     //a list of services with their features
    int16      mapSize;
    int16      mapTop;
    PidHandle *maps;    //a vector
};

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

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

struct PidHandle {
    pid_t pid;      //client EP 所在进程的 pid
    uid_t uid;      //client EP 所在进程拥有者的 uid
    uint32 handle;  //client EP 的boss线程的tid 或者 tid经过内核映射的编号
    uint32 deadId;  
};

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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

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

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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

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

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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

 

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

Hi3516的SAMGR--系统服务框架子系统-10-Client与Server的IPC来往-鸿蒙开发者社区

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

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

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
已于2021-7-5 07:31:22修改
4
收藏 2
回复
举报
3条回复
按时间正序
/
按时间倒序
liangkz_梁开祝
liangkz_梁开祝

累死我了~~~码字不易啊~~制表画图也不易~~

2
回复
2021-7-2 17:53:50
Whyalone
Whyalone

辛苦辛苦,加一个鸡腿先!🍗

回复
2021-7-2 18:05:50
liangkz_梁开祝
liangkz_梁开祝

还有一点收尾,赶紧写完,我要放个假休息一下。

回复
2021-7-2 18:26:46
回复
    相关推荐