Binder
进程空间划分
- 用户空间:不可共享空间
- 内核空间:可共享空间,所有进程共用一个内核空间
用户空间和内核空间之间的交互
- copy_from_user():将用户空间数据拷贝到内核空间
- copy_to_user():将内核空间数据拷贝到用户空间
Binder模型
角色 | 作用 | 备注 |
---|---|---|
Client进程 | 使用服务进程 | 客户端 |
Server进程 | 提供服务进程 | 服务端 |
ServiceManager进程 | 管理Server注册,查询 | 将字符串形式的Binder名字转换成具体引用 |
Binder驱动 | 1.用于连接Client进程,Server进程以及ServiceManager进程的虚拟驱动 2.通过内存映射(mmap)实现进程之间的数据传递 3.实现线程控制,Binder内部对线程池进行管理 | Binder驱动持有每一个Server进程在内核空间的Binder实例,并提供给Client进程实例的引用 |
工作流程
-
Server进程注册服务
- Server进程向Binder驱动发起注册服务请求
- Binder驱动将Server信息转发给ServiceManager进程
- ServiceManager进程进行注册
-
Client进程获取服务
- Client进程通过服务名称向Binder驱动获取Server服务
- Binder驱动将请求转发给ServiceManager进程
- ServiceManager通过服务名称进行查询,如果查询到就向Binder驱动发送对应的Server服务
- Binder将ServiceManager发送的Server进程返回给Client进程
- 此时Client进程与Server进程建立关系
-
创建接收缓存区
-
Binder驱动创建出接收缓存区
-
通过mmap(),实现地址映射关系
-
将通过ServiceManager查找到的Server进程和内核缓存区与接收缓存区进行内存地址映射
只是建立了内存地址映射,无法传输数据
-
-
-
Client进程开始请求
-
Client进程通过系统进行调用copy_from_user()方法发送数据给内核缓存区,并挂起当前线程
-
Binder驱动通知Server进程进行解包
由于内核缓存区和接收缓存区都与Server进程的用户地址区域存在地址映射,所以通过copy_from_user()方法,就相当于发送到了Server进程的用户地址区域
-
-
Server进程进行处理
-
Server进程通过自己内部的线程进行解包,并且调用目标方法
-
将结果写入至自己的用户地址区域
由于内核缓存区和接收缓存区都与Server进程的用户地址区域存在地址映射,就相当于发送到了内核缓存区
-
-
Client进程获取数据
- Binder驱动唤醒Client进程处理的线程
- Client线程通过系统调用copy_to_user()方法获取内核缓存区映射的Server进程的结果数据
Binder有自己的线程池,内部默认为16个,超出数量的请求会被阻塞,知道有空闲Binder线程处理
Client进程,Server进程,ServiceManager都是用户进程,不能直接调用,都必须通过Binder驱动调用
代码执行流程
- Client进程将需要传输的数据写入到Parcel之中,也就是目标方法的参数以及接口标识符
- 通过代理对象的transact()方法,将数据发送给Binder驱动
- 当前Client线程被挂起
- Binder驱动根据代理对象找到对应的Server进程
- 发通至对应的Server进程,并通知进行数据解包
- Server进程从当前线程池里取出处理线程,调用onTransact()方法进行解包,并通过标识符执行对应的方法,返回结果
- Binder驱动根据代理对象返回结果
- 唤醒Clent进程处理线程,并勇敢代理对象获取到结果
代码流程结构
-
Server进程的定义
- interface是进程间通信定义的通用接口,通过定义接口,然后再服务端实现接口、客户端调用接口,就可实现跨进程通信,内部只有一个方法,返回当前接口关联的 Binder 对象
- 继承自interface的实现接口内部定义需要实现的接口方法,即Client进程需要调用的方法
public interface IIntf extends IInterface { public int fun(int a,int b); } public interface IInterface { public IBinder asBinder(); }
-
Server进程的实现
- interface实现类实现了定义的方法的具体实现,并且返回了IBinder
IInterface iintf = new IIntf(){ //定义的方法 public int add(int a,int b) { return a+b; } //转为Binder public IBinder asBinder(){ return myBinder ; } };
-
Server进程的注册
- 将(parameter,descriptor)作为(key,value)对存入到Binder对象中的一个Map<String,IInterface>对象中
- Binder对象 可根据descriptor通过queryLocalIInterface()获得对应IInterface对象(即plus)的引用,可依靠该引用完成对请求方法的调用
//向Binder驱动注册服务(最终到ServiceManager中注册) binder.attachInterface(iintf,"descriptor");
-
Server进程的处理
- Binder的子类stub实现了onTransact()方法
- 用于执行Client进程所请求的目标方法
- code:Client进程请求方法标识符。即Server进程根据该标识确定所请求的目标方法
- data:目标方法的参数。
- reply:目标方法执行后的结果,最终返回给Client进程
- 当Client进程发起远程请求时,远程请求会要求系统底层调用该方法,运行在Server进程的Binder线程池中
public class Stub extends Binder { @Override boolean onTransact(int code, Parcel data, Parcel reply, int flags){ switch (code) { case Stub.add: { //解包数据 data.enforceInterface("descriptor"); int arg0 = data.readInt(); int arg1 = data.readInt(); //通过参数获取真正执行方法的代理 int result = this.queryLocalIInterface("descriptor").fun(arg0,arg1); //写入返回值 reply.writeInt(result); return true; } } return super.onTransact(code, data, reply, flags); }
- 方法内部首先会通过code进行过滤操作,在指定操作中,通过queryLocalInterface(data.enforceInterface)获取IInterface对象,再把对应的参数获取并传入,最终把返回值写至reply对象中。
-
Client进程获取服务
- 通过bindServer()绑定Server进程中的Server
- 在onServiceConnected()回调之中,得到BinderProxy对象
private void bindRemoteService() { // 远程服务具体名称 ComponentName componentName = new ComponentName(this, "com.me.dmn.mybinder.server.MyService"); Intent intent = new Intent(); intent.setComponent(componentName); // 绑定到服务 bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { // 持有binder的引用 mBinder = iBinder; tvConnectState.setText("Connected"); } @Override public void onServiceDisconnected(ComponentName componentName) {} };
-
Client使用服务
- 将需要传送的数据写入到Parcel对象中,目标方法的参数+IInterface接口对象的标识符descriptor
- 通过调用代理对象的transact() 将数据发送到Binder驱动,在发送数据后,Client进程的该线程会暂时被挂起
- Binder驱动根据代理对象找到对应的真身Binder对象所在的Server 进程(系统自动执行)
- Binder驱动把数据发送到Server 进程中,并通知Server进程执行解包(系统自动执行)
- Binder驱动根据代理对象沿原路将结果返回并通知Client进程获取返回结果,之前挂起的线程被唤醒
//将需要传送的数据写入到Parcel对象中 android.os.Parcel data = android.os.Parcel.obtain(); data.writeInt(1); data.writeInt(2); data.writeInterfaceToken("descriptor");; android.os.Parcel reply = android.os.Parcel.obtain(); //将数据发送到Binder驱动 binderproxy.transact(Stub.add, data, reply, 0) //获取数据 binderproxy.transact(Stub.ADD, data, reply, 0); reply.readException(); result = reply.readInt();