Binder优点:
1.性能方面:
Binder拷贝数据需要一次(基于mmap实现),而传统的Linux/管道等IPC方式需要拷贝两次数据(基于普通文件读写拷贝,需要页缓存过渡),虽然共享内存方式不需要进行数据拷贝,但是实现方式比较复杂;
binder通信传输数据大小: binder通信单次传输数据大小不超过1M(上层用户空间),内核空间4M;
Bundle传输数据大小: 单个Bundle不能超过50KB。整个进程内所有的bundle共享内存大小不能超过1MB
因为binder通信内部数据拷贝是通过mmap实现的, mmap函数会为Binder数据传递映射一块连续的虚拟地址,这块虚拟内存空间其实是有大小限制的,不同的进程可能还不一样;普通的由Zygote孵化而来的用户空间进程,所映射的Binder内存大小是不到1M的,准确说是 110241024) - (4096 *2)
进程通信方式:
Linux的进程间通信的方式: 管道/socket/消息队列,Binder,共享内存;
2.安全方面:
由于传统的IPC接收方无法获得对方进程的UID/PID,无法鉴别对方身份;Android中为每一个应用程序分配了单独的UID,所以Binder是相对更安全的;
Binder架构:
前提知识:
一个进程空间分为了 用户空间和内核空间;
进程间,用户空间数据不能共享,内核空间数据可共享:
Binder结构:
Client进程:
使用服务的进程,类似于Android客户端;
Server进程:
提供服务的进程,类似服务端;
ServiceManager进程:
注册与查询服务作用,类似路由器;
Binder驱动;
一种虚拟设备驱动,链接Server进程,Client进程,ServiceManger的桥梁:(transact方法是关键,通过它进行传输)
1.传递进程间的数据:
a.当Client端向Server端发起IPC请求,Client端把请求数据从用户空间拷贝到内核空间;
b.Binder驱动将内核空间的数据拷贝到Server端所在的用户空间;
2.实现线程管理:采用Binder的线程池,并由Binder驱动自己进行管理;
Binder通信流程图:
流程说明:
1.注册服务:
a.Server进程向Binder驱动发起服务注册请求
b.Binder驱动将注册请求转发给ServiceManager进程
c.ServiceManager进程帮Server进程注册成功,此时ServiceManager进程将获取到Server进程信息;
2.获取服务:
a.Client向Binder驱动发起获取服务请求,传递服务的名称;
b.Binder驱动将请求转发给ServiceManager进程
c.ServiceManager进程根据服务名称找到Client需要的服务信息;
d.Binder驱动将查询到的服务信息从Server端返回给Client端,此时Client和Server已经建立连接;
3.使用服务:
MyClient进程通过BinderProxy对象操作Binder驱动内的Binder对象,进而操作MyServer进程
1. MyClient进程将参数(整数a和b)发送到 MyServer进程
2. MyServer进程 根据MyClient进程要求调用 目标方法(即加法函数)
3. MyServer进程将目标方法的结果(即加法后的结果)返回给MyClient进程
上述流程中的额外说明:
1.Binder驱动和ServiceManager进程属于Android基础架构(系统已经实现好了的),Client和Server属于Android应用层(开发者自己实现);
2.Binder的线程管理:Server会创建很多线程来处理Binder的请求,而这些线程是属于Binder线程池的,且归Binder自身管理,默认线程最大数量是16;
自己实现Binder:
和通过AIDL接口实现Android进程通信一样;
首先创建一个Activity中的service类,来获得server对象,也就是IBinder对象;只不过获得这个server对象要继承Binder,重写onTransact()方法;
当客户端发起请求的时候,Binder Driver就会调用execTransact 方法间接地会转到到server端调用onTransact();Binder内部的native()方法实现了线程池,但还是要保证线程安全;
**onTransact(int code, Parcel data, Parcel reply, int flags):**这就是一个比较核心的方法了,它主要是用来执行远程调用的目标方法,执行完后把返回值写入reply中。这个方法是运行在服务端的Binder线程池中,客户端发起请求的时候,会通过系统底层封装后交给此方法来处理,如果返回false,那么客户端将请求失败(调用不到所请求的方法)。参数如下:
**code:**即AIDL接口中方法的唯一标识,通过它可以确定调用的是那个方法。
**data:**客户端远程调用方法的时候传过来的参数的值被封装进了这个data,需要就可以取出。
**reply:**当前方法的返回值会被封装进reply。
**flags:**只有0和1(FLAG_ONEWAY)两种取值,取0表示同步操作(默认),取1表示客户端单向操作,无需等待服务端响应。
其次,创建一个Activity中的客户端;在其中调用获得的IBinder对象来获得从service类中传来的数
据;
只不过解析的时候其中多了一个IBinder对象.transact来获取值得方法;
传统跨进程通信和Binder区别
我们知道为了保证进程的安全性与独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的。
如果进程间需要进行数据交换和通信就需要用到跨进程通信(IPC)。跨进程通信有两种方式,传统数据拷贝方式与mmap方式。
1. 传统的跨进程通信
发送进程通过系统调用,将需要发送的数据从用户空间copy_from_user拷贝到Linux进程的内核空间中(Page Cache)
内核服务程序唤醒接收进程的接收线程,通过系统调用将数据从内核空间中copy_to_user发送到用户空间中
接收进程获得用户空间中的数据,完成了数据的跨进程通信
缺点:
效率低下,需要2次数据拷贝, 用户空间>>内核空间>>用户空间
2. mmap跨进程通信
以Android中Binder IPC跨进程通信为例
第一步、调用进程通过系统调用进入内核态数据交互这个步骤是拷贝了进程间所需要数据的指针,指针将指向和发送进程相同的一块缓存区物理地址;
第二步、在内核态将发送进程的数据拷贝到俩进程共同申请的缓存区物理地址内存中,这个步骤确实发生了进程间所需要数据拷贝;
第三步、对应的进程再将数据从内核态拷贝到用户态,这个步骤拷贝的同样是数据指针。
这样总共经过3次数据交互,将数据从调用进程传递到目标进程。其实也是经过了3次数据拷贝,只不过第一次和第三次拷贝的都是Binder机制相关数据结构所需要的数据的拷贝,只有第二步发生了将进程间所需要数据的实际拷贝
优点:
- 传输效率高,拷贝次数1次,用户空间可以直接通过共享对象直接交互
以下是之前很早做的笔记,略微参考一下
1.Binder的机制