一、 linux 进程间通讯概述
- 管道:在创建时分配一个 page 大小的内存,缓存区大小比较有限;
- 消息队列:信息复制两次,额外的 CPU 消耗;不合适频繁或信息量大的通信;
- 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- 套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
二、binder 概述
2.1 为什么 android 还要用 binder
Linux提供有管道、消息队列、信号量、内存共享、套接字等跨进程方式,那为什么Android要选择Binder另起炉灶呢?
2.1.1 传输性能好
- Socket:是一个通用接口,导致其传输效率低,开销大
- 共享内存:虽然在传输时不需要拷贝数据,但其控制机制复杂
- Binder:复杂数据类型传递可以复用内存,需要拷贝1次数据
- 管道和消息队列:采用存储转发方式,至少需要拷贝2次数据,效率低
2.1.2 安全性高
- 传统的进程:通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设
- Binder机制:从协议本身就支持对通信双方做身份校检,因而大大提升了安全性
2.2 binder 是在 android 上进程间通讯的一种方式
结构图
- Binder通信机制采用C/S架构;
- Binder框架中主要涉及到4个角色Client、Server、Service Manager及Binder驱动,其中Client、Server、Service Manager运行在用户空间,Binder驱动运行在内核空间
- Client代表客户端进程,Server代表客户端进程提供各种服务
- Service Manager用来管理各种系统服务
- Binder驱动提供进程间通信的能力
- 用户空间的Client、Server、ServiceManager通过open、mmap和ioctl等标准文件操作来访问/dev/binder,进而实现进程间通信。
2.3 通讯原理
- Service 端通过 Binder 驱动在 ServiceManager 的查找表中注册 Object 对象的a dd 方法
- Client端通过Binder驱动在ServiceManager的查找表中找到Object对象的add方法,并返回 proxy 对象的add方法,add 方法是个空实现,proxy 对象也不是真正的 Object 对象,是通过 Binder 驱动封装好的代理类的 add 方法
- 当Client端调用 add 方法时,Client端会调用 proxy 对象的 add 方法,通过 Binder 驱动去请求ServiceManager 来找到 Service 端真正对象,然后调用 Service 端的 add 方法
2.4 Binder 对象和 Binder 驱动
Binder对象:Binder机制中进行进程间通讯的对象,对于 Service 端为 Binder 本地对象,对于 Client 端为 Binder 代理对象。
Binder驱动:Binder 机制中进行进程间通讯的介质,Binder 驱动会对具有跨进程传递能力的对象做特殊处理,自动完成代理对象和本地对象的转换。
2.5 binder 每一层的分析
- 从IPC角度来说:Binder是Android中的一种跨进程通信方式,该通信方式在linux中没有,是Android独有;
- 从Android Driver层:Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder;
- 从Android Native层:Binder是创建Service Manager以及BpBinder/BBinder模型,搭建与binder驱动的桥梁;
- 从Android Framework层:Binder是各种Manager(ActivityManager、WindowManager等)和相应xxxManagerService的桥梁;
- 从Android APP层:Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的 Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。
2.6 Binder架构
2.7 framework层
1. 采用JNI技术来调用native(C/C++)层的binder架构,从而为上层应用程序提供服务。
2. 红色代表整个framework层 binder架构相关组件;
- Binder类代表Server端
- BinderProxy类代码Client端;
3. 上层framework层的Binder逻辑是建立在Native层架构基础之上的,核心逻辑都是交予Native层方法来处理。
4. framework层的ServiceManager类与Native层的功能并不完全对应,framework层的ServiceManager类的实现最终是通过BinderProxy传递给Native层来完成的。
2.8 Binder类图
2.8.1 类(class)
图中白色部分
- ServiceManager:通过getIServiceManager方法获取的是ServiceManagerProxy对象; ServiceManager的addService, getService实际工作都交由ServiceManagerProxy的相应方法来处理;
- ServiceManagerProxy:其成员变量mRemote指向BinderProxy对象,ServiceManagerProxy的addService, getService方法最终是交由mRemote来完成。
- ServiceManagerNative:其方法asInterface()返回的是ServiceManagerProxy对象,ServiceManager便是借助ServiceManagerNative类来找到ServiceManagerProxy;
- Binder:其成员变量mObject和方法execTransact()用于native方法
- BinderProxy类:是Binder类的内部类,它代表远程进程的Binder对象的本地代理
- BinderInternal:内部有一个GcWatcher类,用于处理和调试与Binder相关的垃圾回收
2.8.2 接口(Interface)
图中蓝色部分
- IBinder:接口中常量FLAG_ONEWAY:客户端利用binder跟服务端通信是阻塞式的,但如果设置了FLAG_ONEWAY,这成为非阻塞的调用方式,客户端能立即返回,服务端采用回调方式来通知客户端完成情况。另外IBinder接口有一个内部接口DeathDecipient(死亡通告)。
- IInterface接口:client端与server端的调用契约,实现这个接口,就代表远程server对象具有什么能力,因为IInterface接口的asBinder方法的实现可以将Binder本地对象或代理对象进行返回
2.9 Binder类分层
三、Binder内存机制
虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间间。
当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。
一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。
Binder在进程间数据通信的流程图,从图中更能明了Binder的内存转移关系。