Android中进程间通信Binder机制知识点总结

IPC定义

广义上讲,进程间通信(Inter-process communication IPC)是指运行在不同进程(不论是否在同一台机器上)中的若干线程间的数据交换,

背景

我们知道,在操作系统中各个进程通常运行在独立的内存空间中,并且有严格的进程隔离机制来防止进程间的数据访问,因为不这样做就会引起很多的数据问题,这里会涉及到进程空间的概念:

  • 进程空间分为用户空间,内核空间
  • 用户空间的数据是隔离的,每个进程占用一个用户空间,不能相互访问
  • 内核空间是公用的,数据可以相互访问,所有的进程共用一个内核空间
  • 同一个进程内的用户空间和内核空间通过系统调用相互访问
  • 系统调用主要通过Linux中的copy_from_user()方法将数据从用户空间拷贝到内核空间,copy_to_user()将内核空间数据拷贝到用户空间

但是问题又来了,那两个进程间怎么通信呢?其实传统的进程间通信机制的原理就是:

  • 发送进程中的线程通过系统调用将数据拷贝到内核空间的缓存区
  • 内核空间中的服务程序通知接收进程里的线程
  • 然后接收线程通过系统调用将数据拷贝到自己的用户空间

实现方法有如下几种:

共享内存(shared Memory):

这是一种常用的进程间通信机制,由于两个进程可以直接共享访问同一块内存区域,减少了数据的复制操作,因而在速度上有优势,实现方式:

  1. 创建内存共享区:进程A通过操作系统提供的API从内存中申请一块共享区域,比如Linux中可以通过shmget函数来实现,需要传入三个参数,分别是key、size、shmflg;有个返回值是内存共享区域的id值,用于唯一标识该区域;该方法生产的共享内存将于某个特定的key(第一个参数)进行绑定
  2. 映射内存共享区:成功创建内存共享区后,把它映射到进程A的空间中,在Linux中可以通过shmat函数实现,有三个参数shmid(第一步的返回值)、shmaddr、shmflag;同时有一个返回值,表示内存区的起始地址
  3. 访问内存共享区:进程B通过shmget函数,传入同样的key,然后再调用shmat函数即可将内存映射到自己的空间
  4. 开始通信:两个进程都进行了内存映射后就可以利用该区域通信了,但是内存共享没有同步机制,往往与信号灯等同步机制共同使用
  5. 删除内存共享区:通信结束后需要回收内存,在Linux中通过shmctl函数实现,有三个参数shmid、cmd、buf;有一个返回值表示删除成功还是失败
管道(Pipe):

管道也是操作系统中常见的一种进程间通信方式,Pipe这个词很好的诠释了通信双方的方式,

  1. 进程A和进程B位于管道两边,进行数据的传输
  2. 管道中数据是单向的,即管道只运行数据从一边进,从另一边出,这就跟水管一样
  3. 管道有容量限制,当容量满的时候,写操作将阻塞,反之读操作也会阻塞
  4. Linux中可以通过pipe(int pipefd[2] , int flags)函数打开管道,pipefd[0]表示读端,pipefd[1]表示写端
远程过程调用(Remote Procedure Calls)

RPC涉及的通信双方通常运行在不同的机器中,在RPC机器中,开发人员不需要关心具体的中间传输过程是如何实现的,实现步骤:

  1. 客户端进程调用Stub接口
  2. Stub根据操作系统要求打包
  3. 操作系统内核来完成与服务器端的具体交互,主要负责将客户端的数据包发送给服务器端的内核
  4. 服务器端Stub解包并调用与数据包相匹配的进程
  5. 服务器端进程执行操作并可将结果返回

还有Socket,消息队列,信号量等其它多种方式,这里不多赘述了。

使用Binder原因

但是Android并没有采用这些通信方法而选择了Binder(其实有少许地方使用到了其它方式,比如Zygote在fork进程的时候使用的是Socket),为什么呢,个人理解为

  • 性能:在早期移动设备上,各种资源是有限的,特别是内存,CPU资源,电量等,所以对各种进程间通信机制的性能要求高,而Binder的实现机制更加高效;一些传统进程通信机制使用了两次用户空间和内核空间的数据拷贝,比如管道、消息队列、Socket都需要2次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂且没有同步机制;而Binder只用了一次,主要是因为使用了内存映射,底层IPC机制多使用一次数据拷贝其实对整体性能影响很大
  • 安全:由于传统的进程通信机制没有对通信双方做严格的身份验证,比如Socket就在上层应用时就得指定ip地址,这都是有安全隐患的;而在使用Binder时,这些身份标示是跟随调用过程而自动传递的。Server端很容易就可以知道Client端的身份,非常便于做安全检查
  • 架构:Binder是基于C/S架构的,更符合Android系统架构

这里有一个内存映射的概念,它的出现也是因为传统进程通信方式的弊端;我们知道操作系统分为用户态和内核态,而用户态不能直接与硬件打交道,需要走硬件—>内核—>用户,这就出现了两次拷贝;使用了内存映射后,就只用一次拷贝,大大提高效率。

解释内存映射就要先说虚拟内存,说虚拟内存的概念先说为什么需要虚拟内存

虚拟内存的由来:

我们知道程序代码和数据需要被解释在内存中才能得以运行,早期计算机内存容量是比较小的,但是当时的程序更小,基本都是很简单的那种,所以把程序直接解释到内存中没有问题,不影响程序运行;但是随着互联网的飞速发展,计算机中装的软件越来越多,而且软件也越来越大,如果这时候还像以前那样把程序全部都解释到内存中,显然这种方式是行不通的,将会导致很多程序无法得到很好的运行,何况后来多任务的操作系统。
有什么解决办法呢,第一个想到的就是切片,将程序分成片段,只让当前需要的那一部分留在内存中,其它的放在外部存储上,执行结束后再调下一个需要运行的片段;在以前的一些古董系统里靠这方法解决一些大程序,并且这个分片工作是由程序员完成的;但是时代在发展,各种语言层出不穷,操作系统也越来越透明,程序员对底层技术的依赖逐渐消失,谁还能这样去操作系统呢;现在还让程序员这样做,不光程序难以运行,可能把机器都能搞蹦,所以系统需要一种新的按需分配内存且不用程序员管理的技术,就这样虚拟内存技术就应运而生

虚拟内存

计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换

虚拟内存原理

虚拟内存是将系统硬盘空间和系统实际内存联合在一起供程序使用,给程序提供了一个比实际内存大得多的虚拟空间。比如Win操作系统,只有4个G内存,然后把系统盘通常是C盘作为外部存储,这样就能给程序提供远大于4个G的虚拟内存使用。在程序运行时,只把虚拟内存的一小部分存储到内存,其余都存储在硬盘上(也就是说程序虚拟空间就等于实际物理内存加部分硬盘空间)。当被访问的虚拟地址(也就是虚拟内存对应的地址)不在内存时,则说明该地址未被存储到内存,而是被存贮在硬盘中,因此需要的虚拟地址随即被调入到内存;同时当系统内存紧张时,也可以把当前不用的虚拟内存换出到硬盘,来腾出物理内存空间。系统如此周而复始地运转——换入、换出,而用户几乎无法查觉,这都是拜虚拟内存机制所赐。

现在来介绍内存映射的概念

学习数学的时候肯定学过映射的概念,映射就是将两个事物建立一种一一对应的关系,只是逻辑上的一种关系,在物理上是不存在的;就是说你脑海中的想象,并不是真实存在的。
在这里就是把程序的虚拟内存的一块区域与某物理内存块之间建立一一对应的联系,这就是映射,然后操作程序的虚拟内存就是操作这块物理内存,就不需要经过内核转换;在内存映射过程中,并没有实际的数据拷贝,只是逻辑上放入了内存,具体到代码,就是建立并初始化了相关的数据结构,这个过程由系统调用mmap()实现,所以映射的效率很高。

  • 我们这里以文件为例:把硬盘文件内容映射到程序的虚拟内存中,通过对虚拟内存的读取修改就是对文件的读取修改,这样进程就可以像访问普通内存一样访问文件,而不用调用read()、write()等操作了
  • 再以进程为例,分别把进程A和进程B的虚拟内存区域的一块与共享内存C建立映射关系,假如进程A对自己的这块虚拟内存区域进行修改,那么也会映射到进程B的虚拟内存块;因为这两个进程都映射到了同一个共享内存C,所以A的修改对于B是可见的

上面说这么多Linux相关的知识都是为了Binder机制做铺垫


Binder组成

我们知道,Binder是Android中使用最广泛的IPC机制,那作为开发者怎么理解Binder这个东西呢,从字面理解为【捆绑】,也就是将两个需要相互联系的对象用某种力量绑定在一起,那这些参与进来的对象有哪些呢?

  • Binder驱动: 一种虚拟的设备驱动,连接Client、Server、ServerManager的桥梁;Binder驱动保存了每个Server进程在内核空间中的Binder实体,并给Client进程提供Server进程的Binder实体的引用
  • Service Manager:主要管理进程注册与查询,并保存进程信息;每个对外公开服务的进程都需要到这里注册,注册成功会被分配一个唯一id;类似于派出所会保存每个人的信息,给每个人分配一个身份证号码,车管所保存每辆车信息,给每个车分配一个车牌号
  • Binder Client:使用Server的进程
  • Binder Server:提供Server的进程

Binder机制就是把这些东西绑定在一起,其中Client、Server和Service Manager运行在用户空间,Binder驱动程序运行内核空间

Binder中的核心组件就是Binder驱动,Service Manager提供了辅助管理的功能,Client和Server正是在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信

四个组件中的Binder驱动和Service Manager,Android已经替开发者封装好了,开发者只要写好Client和Server就可以了

类比理解

从上面的分析是不是感觉Binder原来就这么回事,实际上我感觉Binder是Android系统中最难理解的的一个机制了;那怎么以一个通俗易懂的例子来理解呢?

从组成Binder机制的的四个组件可以看出来,这其实与我们天天接触的TCP/IP网络非常相似

  • Binder驱动 可以理解为 Router路由器
  • Service Manager可以理解为 DNS服务器
  • Client 可以理解为 客户端,比如浏览器
  • Server 可以理解为 服务器

现在就客户端从浏览器访问Google的例子我们来分析

  1. 当我们在Client端浏览器输入google.com后,它会先通过DNS服务器查询该网站的IP地址(当然Client得先知道DNS的IP地址才能发起查询,这是在客户端接入网络就设置好了的);如果Client已经知晓了Server的IP,那就不需要DNS查询这一步了,直接去与Server链接。比如Window系统下有一个Host文件,用于查询常用网址与其对于的IP,当用户访问这个网址的时候就不要再去DNS查询了,就可以提高访问速度(当年还用过改host文件达到翻墙的功能)
  2. DNS服务器将查询结果返回Client,至于怎么返回,Client的IP地址都封装在了TCP/IP包中,在查询的时候发送给了DNS
  3. Client得到Google ip后就可以发起连接

这些步骤中没有提到Router,但是它的作用很重要,将数据包发到用户设定的目标IP中,也会获取Server端发送过来的数据包转发给Client端,所以Router可以说是整个网络通信的基础

在这里我们知道DNS其实不是必须要每一个完整通信过程都要存在的,它只是帮人们将繁杂的IP和好记的域名联系起来

从这个通信过程中我们可以看出IP地址对于每个用户来说是唯一的,是Client和Server进行通信的凭证

看懂了这个例子后,我们把它与Binder机制进行类比

Binder的本质就是进程A(客户端)要与进程B(服务器)进行通信,但是因为是跨进程(跨网络),所以必须借助于Binder驱动(路由器)来把请求正确发送到对方所在进程中,而通信的进程们需要持有Server Manager(DNS)颁发的唯一标志(IP)。
类比于TCP/IP网络,其实Binder当中的DNS也不是必须的,因为如果客户端进程能记住服务端进程的Binder标志(IP),就不需要通信前通过查询DNS了。
但是这个通信标志其实是动态改变的,也就是说即使客户端进程记住了这一次通信的服务端进程的标志,但下一次就不管用了,所以还是需要Server Manager来统一管理双方进程的标志。

上面的类比理解中,提到了其实Client端和Server端也是需要知道DNS服务器的ip的,要不然没办法给DNS发请求进行查询,所以在网络接入的时候提前设置好;到Binder机制中,Server Manager是DNS,那它的IP是多少呢?Binder机制做了特别规定,Server Manager在Binder通信过程中的唯一标志永远是0。


进程通信的实现

在详细叙述Binder原理之前我们先看下怎么使用Binder来达到进程间通信,具体步骤:

  • Server进程通过Binder驱动向ServiceManager进程进行注册,在ServiceManager进程中保留自己的Binder对象
  • Client向Binder驱动发起获取服务的请求,并传递要获取的服务名称
  • Binder驱动将该请求转发给ServiceManager进程
  • ServiceManager查找到Client进程需要的Server进程对应的Binder实体,然后返回给Binder驱动
  • Binder驱动将上述Binder实体的代理对象返回给Client,要知道这里Client进程拿到的并不是真正的Server进程的Binder对象,而是Binder对象的代理对象

开发者实现跨进程通信主要是通过AIDL来实现的,具体实现请看这篇文章
Android四大组件之Service 远程服务解析,通过AIDL达到进程间通信交换基础数据

静待更新

猜你喜欢

转载自blog.csdn.net/qq_30993595/article/details/81206781