美文网首页
像创造者一样去思考跨进程通信Binder

像创造者一样去思考跨进程通信Binder

作者: 蚍蜉一生 | 来源:发表于2024-08-15 19:52 被阅读0次

唯有一个造物主方能真正理解另一个造物主!

在《跨进程通信破障一击》中有介绍到,Binder是Android系统中的一种高效、安全的跨进程通信方案。

若想深入理解Binder,那我们就尝试创造一种跟Binder类似的跨进程通信方案,在此过程中来思考Binder的真谛~

与Binder类似是指:我们设计的跨进程通信需要与Binder机制所处环境相同,功能一致!

现在看下,binder所处环境和功能要点

binder机制
如图所示,Binder运行的环境是一个Android系统的内部,它的作用是使一个进程的方法C1能够调用另一个进程的方法S1,并拿到S1的执行结果。

除了上面两个条件,跨进程通信还有一个默认条件,就是跨进程通信一定需要经过内核中转。

跨进程通信破障一击中,我们了解到构建一个跨进程通信方案并不难,不过是通信双方协商好一个都可以触及的地方,发送方投放消息、接收方读取消息即可,这是跨进程通信的基本原理,我们可以根据这个原理完成跨进程通信的基本设计。

但仅有基本通信功能还不够,我们需要的是能够快、稳、安全地跨进程通信!

so~我们先进行基本通信功能设计,然后在此基础上融入性能、稳定性、安全方案。

——————现在开始我们的设计——————

一、跨进程通信基本功能设计

跨进程通信就是在整个系统范围内,进程A远程使用进程B的特定方法f()。这里有几个关键问题需要考虑:

1. 进程A如何知道进程B有方法f(),f()有哪些功能以及如何使用;

2. 方法f()在整个系统范围内唯一定位是什么样子的;

3. 进程A如何得到方法f()的唯一定位;

4. 进程A调用f()的信息在跨进程通信时候如何封装,进程B得到A的信息之后如何解封装得到调用信息;

5. 进程A如何发送封装数据,进程B如何接收封装数据;

6. 进程B的方法f()执行结果怎么回传给进程A。

—————接下来,我们针对这些问题来逐个思考————

1.1 进程A如何知道进程B有方法f()、方法f()有哪些功能以及如何使用?

让另一方知道你有什么方法以及方法如何使用,这个问题很常见,做开发的经常遇到的,想一想~

进程B想让进程A知道它方法f()的功能以及如何使用,就是把方法f()定义的代码或者文档暴露给进程A和开发者。

这些对外的方法,一般叫做接口,了解一个接口一般需要通过接口定义的代码和接口文档,哈哈~

常见的网络请求、驱动、系统调用等相关开发,都需要拿到被调用方接口定义代码或者接口文档,跨进程通信亦需如此。

拿到方法f()定义的代码或者接口文档后,一般有两种处理方式:

  1. 直接将f()定义的代码复制到程序A中,它在A中的使用跟B中一样。这种方法有一个前提是,进程A和进程B使用的编程语言一样,且运行环境一致;

  2. 理解方法f()的定义后,在程序A中重新构建一个新的方法g(),g()的定义包含了远程调用f()需要的地址、参数类型等;

其实这两种方式本质上是一样的,都是在了解f()后在客户端构建代理,不同的是,一种代理的实现用了跟实际方法定义完全一样的代码而已。

这里还有一个思考题:
目前我们遇到的场景都是——服务端方法定义是提前确定好的,所以我们能在编码阶段能把代码复制过去并设计好相应的代理;如果未来,服务端的方法是AI临时生成的,那我们又该如何设计这个环节呢?

1.2 在一个系统内,一个方法的定位方案是怎么样的?

在一个系统内,定位方法一般有两种:

  1. 基于组织架构或者层级结构的定位;

  2. 基于统一的地理位置的定位;

比如我们想在地球仪上找到黄山,有两种方式:

  1. 按照中国/安徽省/黄山市/黄山景区层层来找;

  2. 根据黄山经纬度坐标北纬30.1°,东经118.1°来查找

第一种定位方式可称之为多层相对位置定位法,就是说你只需要知道每一层在上一层的哪个位置就好,无需知道该层在整个系统的位置,这样通过一层层的相对位置查找,最终就能找到你的目标。这种方式跟系统的组织架构和层级划分息息相关,系统层级确定后,这种方式就默认存在,它定位的时候查找次数跟层级相关;

第二种定位方式也可以称之为全局绝对定位法,它需要在系统内有一个不依赖层级的坐标系统,它可以根据坐标一次查找到具体位置,但这种系统内绝对坐标是不容易得到的,需要系统设计者在设计时就考虑全局坐标,也需要使用者对系统内在运行机制有深刻了解。

实际的定位方案既可以单独使用定位方案1,2,也可以两者两者融合使用!

在Android系统中,我们可以用方案1,即遵循 进程/类或者对象/方法 来寻找一个方法;也可以使用1+2,从系统直接获取到方法所处的类或者对象(Activity、Service、Binder等),再找到这个对象的方法,这是Android系统常见的一种定位做法。

1.3 进程A如何得到进程B方法f()的唯一定位

在上一节的最后,我们有提到Android系统中方法定位路径是进程/类或者对象/方法 ,那其他进程是如何得到这个定位路径呢?

我们来剖析下这个路径: “进程/类或者对象/方法”

首先是进程,进程跟包名是一一对应的,在编码时候我们就能知道其他进程的包名;在运行时候,Android系统有记录包名和进程ID对应的服务,可以根据这个检索出进程ID;

最后是方法名,这个前文1.1也说过,通过代码暴露或者文档暴露直接给到了调用方;

这两个都唾手可得,所以定位的关键在于获取方法所处的对象或者类,这里分两种情况:

1. 服务方进程B是系统进程:固定基准+公示:

    第一 ,我们在App开发的时候,都是需要先了解Android FrameWork有哪些类、有哪些方法,方法怎么用,这就是第一层公示;

    第二, 我们常说的AMS,PMS,这些所谓的服务,不过是运行在系统进程SystemServer里面的一个个对象,Android系统内部维持了一个公示板或者叫服务目录-ServiceManager,这个地方可以查到所有系统服务的定位;

    第三,使用系统服务的时候,首先拿到ServiceManager服务,然后从这个公示服务根据一个id来查找其他服务,那怎么找到ServiceManager服务呢——固定位置,就是说ServiceManager服务起来之后,它会放在一个固定且大家都知道的位置。

    综合以上三点,一个进程获取系统服务的方法内部逻辑就是,先去固定位置找到ServiceManager服务,然后使用服务标识ID从ServiceManager中检索出来服务实例地址,拿到服务实例地址就可以与它通信来使用它了。

2. 服务方进程B是非系统进程:通过系统管理的组件或者服务中转:

    在一个进程内,跨进程通信都需要经过内核,内核的操作权限都归属Android系统进程,所以应用进程之间通信必须通过系统进程中转,一个应用进程获取另一个应用进程的对象或服务,也必须通过系统中转。

    Android系统除了管理每个进程,还管理进程中的四大组件(Activity、Service、BrocastReceiver、ContentProvider),也就是说系统提供了一个进程和其他进程的四大组件之间的通信方案。这个通信方案其实就是一个进程在创建四大组件的时候,像系统服务一样把它的定位注册到系统中,系统保存下来,供其他进程来查询,但是它并没有写到系统公示板中,需要查询方开发者通过API文档获取查询ID,当然这个查询需要也相应的权限。

    一个进程是可以通过系统提供的方法获取到其他进程四大组件的定位并通信;但是四大组件的方法是系统确定好的,如果一个想自定义一个服务方法,可以讲该方法放在四大组件一个对象中,然后通过四大组件/对象/方法来使用该方法。

1.4 跨进程通信信息的封装和解封装;

    除了共享内存,其他跨进程通信都需要进行信息的封装和解封装,这就是序列化, 请参考《序列化应该这样理解》一文,这里不再论述。

1.5 跨进程数据的发送和通知

    在前面有介绍到,封装好的跨进程信息,都是由发送方发送到内核,接收方从内核读取。那接收方如何知道什么时候来读取,用几个线程来读取呢?一般有三种机制:轮询、中断,通知。

    轮询就是服务端过一度时间来查看是否有信息发送过来;

    中断是Linux系统机制,它基本原理是线程运行到中断点就休眠,等待内核说有信息过来后唤醒继续执行,它适合的是简单的顺序控制流,等待明确条件的任务;

    通知则是相当于内核中引入一个邮局,邮局知道谁发了什么信息给到哪个服务对象,通知对方来取,甚至可以通知对方信息很多,你多带几个人来取,它适合多并发的事件驱动任务;

跨进程通信采用哪种接收信息方案要根据具体场景选择其中一种方式,或者综合使用。

1.6 服务方执行结果回传

    前文有讲到,跨进程通信必须内核,要做跨进程通信,内核中需要维护一个地址映射关系,也就是说它知道建立通信后发送方和接收方地址,所以服务端可以把执行结果,通过系统方法,告诉内核后,内核就会帮我们返回给等待的进程,等待的进程有一个对象接收返回结果。

二、性能思考和设计

跨进程通信性能思考有两个维度:

1 通信数据流转全路径:如何最小化请求发出到响应返回的时间;

    一个信息从发送方经过内核到接收方,有多个耗时点,比如上下文切换,系统调用、数据copy、同步方案、通知机制等等。在完成基本的功能设计后,我们就需要思考如何减少进程和线程上下文切换次数,如何减少数据copy次数,如何避免同步等待卡死,如何设计通知机制来保证通知及时高效等。

2 系统资源开销:如何最小化资源(CPU、内存、内存带宽);

    上面有各个节点了,把各个节点的资源消耗加起来就是总共资源消耗了,优化就有两个思考方向,减少节点,降低每个节点的资源消耗。

数据流转路径就是思考的线路,资源限制则是整体优化和局部优化的指导

三、可靠性建设

    可靠性主要是考虑通信过程中各种异常情况怎么处理的问题。比如,双方通信时候,接收方不运行;信息传输一半,有一方突然崩溃;接收方收到消息不处理等等。

    这个需要在完成基本的功能设计后,首先需要把已知的异常考虑在内,来调整整个系统设计,在系统上线后也要不断收集新发现异常情况,不断完善可靠性。

四、通信安全思考

通信安全主要有两点:

1. 数据完整性和隐私保护;

2. 身份认证和权限控制;

    数据完整性和隐私保护主要是通过数据加密、消息摘要等技术实现;身份认证和权限控制一般是通过引入的三方机制实现,每个进程和其他进程通信,都需要调用系统接口,经过内核,所以这个身份验证和权限控制机制就是为各个系统API把门来实现。

————有了以上思考后,我们再来尝试理解下Android 的Binder————
……
……
……
……
……
……

……

……

你明白了吗?

下次,有机会 我们在一起探讨吧~

哈哈——

相关文章

网友评论

      本文标题:像创造者一样去思考跨进程通信Binder

      本文链接:https://www.haomeiwen.com/subject/nbcdkjtx.html