安卓Binder通信原理(1)-概述
Android应用程序是由Activity、Service、BroadcastReceiver和ContentProvider四大组件构成的,这些组件有可能运行在同一个进程中,也有可能运行在不同的进程中,同时Android系统提供的一些服务组件,如Activity管理服务ActivityManagerService和Package管理服务PackageManagerService都运行在系统进程system中,这些运行在不同进程中的应用程序组件和系统服务组件大部分时候都是采用Android系统开发的一套名为Binder进程间通信机制实现通信。
Binder通信机制与传统的进程间通信机制相比,其在进程间传输数据时,只需要执行一次拷贝操作,因此它不仅提高了效率,而且节省了内存空间。
在Binder通信机制中,提供服务的进程称为Server进程,访问服务的进程称为Client进程。Server进程中可以同时运行多个Service组件来给Client进程提供服务,Service组件也称为Service本地对象,Client进程也可以同时向多个Service组件请求服务,每一个请求在Client进程中都对应有一个Client组件,也称为Service代理对象,Client进程正是靠这些Client组件向运行在Server进程中的Service组件发起通信请求。
Server进程和Client进程的通信依靠运行在内核空间的驱动程序来进行,Binder驱动程序向用户空间暴露了一个设备文件/dev/binder使得应用程序进程可以间接地通过它来建立通信通道。Service组件在启动时会将自己注册到运行在servicemanager进程中Service Manger组件中,Client组件可通过Service Manager组件找到Service组件,所以Service Manager组件是Binder进程间通信机制的上下文管理者,同时Service Manager组件也是通过Binder进程间通信机制与普通Server进程和Client进程实现通信的,只不过它是一个特殊的Service组件,下图展示了Server进程、Client进程、Service Manager组件以及Binder驱动程序之间的关系:
Binder通信总览.png大体介绍完Binder进程间通信的概念后,接下来简单描述一下Binder进程间通信流程的整体实现流程。Binder进程间通信依靠内核层的Binder驱动程序以及用户层的Binder进程间通信库来完成,其中用户层的Binder进程间通信库包括C/C++层的Binder接口与Java层中的Binder接口,而Java层中的Binder接口正是通过JNI方法来调用C/C++层的Binder接口以提供进程间通信能力。
Binder驱动程序中定义了很多基础数据结构,主要包括以下重要的数据结构:
1、 binder_node Binder实体对象,代表内核中的Service组件
2、 binder_ref Binder引用对象,代表内核中的Client组件
3、 binder_proc 代表使用Binder进程间通信机制的进程
4、 binder_thread 代表Binder线程
5、 binder_transaction 代表进程间通信过程的事务
6、 binder_work 代表待处理的工作项
7、 binder_transaction_data 代表进程间通信所传输的数据
8、 binder_buffer 代表内核缓冲区
……
Binder进程间通信库C/C++层接口的基础数据结构主要如下:
1、IBinder Binder基础接口,BBinder和BpBinder均继承它
2、BnInterface Binder本地对象,代表C/C++层中的Service组件
3、BpInterface Binder代理对象,代表C/C++层中的Client组件
4、BBinder 为Binder本地对象提供进程间通信接口
5、BpBinder 为Binder代理对象提供进程间通信接口
6、IInterface Service组件接口,描述Service组件提供的功能接口
7、IPCThreadState BBinder与BpBinder通过其与Binder驱动程序交互
8、ProcessState 使用Binder进程间通信机制的进程通过其初始化Binder设备
……
Binder进程间通信库Java层接口的基础数据结构主要如下:
1、IBinder Binder基础接口,Binder和BinderProxy均实现该接口
2、Binder Java服务,代表Jave层中的Service组件
3、BinderProxy Java服务代理对象,为Java层中的Client组件提供进程间通信的能力
4、IInterface Service组件接口,描述Service组件提供的功能接口
……
下图为Binder驱动程序与C/C++层进程间通信库的交互图:
Binder通信交互图.png在Binder驱动程序中,每个Service组件使用binder_node实体对象表示,而Client组件使用binder_ref引用对象表示,驱动程序会为采用了Binder进程间通信的进程生成一个binder_proc对象代表其进程,并为进程的用户空间地址映射内核空间地址,同时在binder_proc中通过user_buffer_offset字段保存进程用户空间地址与驱动程序为其分配的内核空间地址的差值。binder_proc中维护有一个binder_node实体对象的红黑树管理其进程中的Service组件,同样采用红黑树管理进程中的Client组件对应的binder_ref引用对象。binder_node中使用hash列表管理其被哪些binder_ref对象引用,相应的binder_ref中存在一个hash列表结点表示其引用了哪个binder_node实体对象,所以可由binder_ref引用对象查到到binder_node实体对象,进而再由binder_node实体对象中指向用户层中Service组件的地址字段找到Service组件以请求其服务,binder_node与binder_ref中的proc字段指向其所属于的binder_proc。
Server进程中的Service组件需要继承自BnInterface模板类,BnInterface模板类继承了Service组件接口模板,Service组件需要实现Service组件接口中的方法以对外提供服务能力,BBinder类为Binder本地对象提供了与Binder驱动程序通信的接口,而BnInterface模板类继承自BBinder,所以Service组件可通过Binder驱动程序间接与Client组件进行通信。
Client进程中的Client组件需要继承自BpInterface模板类,BpInterface模板类继承了模板Service组件接口以及BpRefBase,BpRefBase中持有一个BpBinder,BpBinder为Binder代理对象提供了与Binder驱动程序通信的接口,所以Client组件可通过Binder驱动程序间接与Service组件进行通信。
当Server进程通过Service Manager注册Service组件时,驱动程序便为Server进程创建一个binder_node实体对象,同时为Service Manager创建一个binder_ref引用对象引用该binder_node实体对象,Service Manager会将该binder_ref引用对象的句柄值以及注册Service组件时传入的服务名进行关联后存在内部的服务列表中。当Client进程通过Service Manager获取Service组件对应的服务时,Service Manager会通过其内部的服务列表得到Service组件关联的binder_ref引用对象的句柄值,驱动程序根据该句柄值可找到Service组件对应的binder_node对象,然后再为Client进程生成一个binder_ref对象引用该binder_node对象,Client进程在得到binder_ref对象的句柄值后,便可创建一个BpBinder对象,进而由BpBinder对象创建Binder代理对象,也即Client组件。
BBinder与BpBinder是通过IPCThreadState实现和Binder驱动程序通信的,IPCThreadState既负责实现Service组件与Client组件向驱动程序发送通信信息,也负责接收处理来自驱动程序返回的通信信息。因为采用Binder进程间通信的进程双方都有一个线程池,所以线程池中的每个线程都有其对应的IPCThreadState对象,通过IPCThreadState对象的transact函数即可向binder驱动程序发送通信数据。Client进程通过IPCThreadState将通信数据封装为binder_transaction_data,并进一步封装到binder_write_read结构体中的输出缓冲区后通过BINDER_WRITE_READ这条IO控制命令发送给Binder驱动程序。由于BpBinder对象持有Client组件的句柄值,所以可由该句柄值找到对应的binder_ref引用对象,进而找到binder_node实体对象,从而知道该通信请求是发送给哪个Server进程的。Binder驱动程序收到该IO命令后,会将来自用户空间的通信数据拷贝到Server进程的内核空间中的binder_buffer缓冲区中,并生成一个binder_transaction事务,事务中保存了binder_buffer,然后将这个事务添加到Server进程或Server进程的binder线程的待处理工作项列表中,并唤醒Server进程或Server 进程的binder线程处理该工作项,驱动程序会根据binder_proc中的user_buffer_offset字段将内核binder_buffer缓冲区转换为用户空间缓冲区,并封装为binder_transaction_data后,进一步封装到binder_writer_read的输入缓冲区后交由Server进程处理,Server进程处理完通信请求后,同样通过BINDER_WRITE_READ IO控制命令将通信结果数据发送给Binder驱动程序,驱动程序可由binder_transaction事务找到发起请求的Client进程,然后通过IPCThreadState对象将通信结果数据返回给Client进程。
当进程向驱动程序发送通信请求时,其需要通过一个命令协议代码表明其请求类型,同样当驱动程序向进程发送返回结果时,也需要通过一个返回协议代码表明其返回结果的类型,命令协议代码与返回协议代码都是由通信协议代码和通信数据构成,命令协议代码的通信协议代码都以BC_开头,返回协议代码的通信协议代码均以BR_开头,例如BC_TRANSATION即代表通常的Binder通信请求,其通信数据通过binder_transaction_data表示,BR_REPLY代表驱动程序向源进程转发目标进程返回给源进程的数据,其通信数据也采用binder_transaction_data表示。其他的命令协议代码还有诸如BC_INCREFS、BC_DECREFS、BC_ACQUIRE、BC_RELEASE用于增减Binder引用对象的强弱引用计数、BC_REQUEST_DEATH_NOTIFICATION用于注册死亡接收通知等等。其他的返回协议代码还有诸如BR_INCREFS、BR_DECREFS、BR_AQUIRE、BR_RELEASE用于增减Binder本地对象强弱引用计数、BR_DEAD_BINDER用于通知Client组件Service组件已经死亡等等。
下图是Binder通讯库为Java层提供的Binder接口与C/C++层接口的关系图
Binder通信库接口关系.png在Java层中,Server进程中的Service组件称为Java服务,而Client进程中的Client组件称为Java服务代理对象,从上图可看到Java服务继承自Java层的Binder类,Java层的Binder对象持有一个JavaBBinderHolder对象,而JavaBBinderHolder对象又持有一个JavaBBinder对象,JavaBBinder继承自BBinder,所以JavaBBinder对象即为Java服务在C/C++层中对应的Binder本地对象。Java服务代理对象持有一个BinderProxy对象,BinderProxy对象又持有一个BpBinder,从而Java服务代理对象可由BinderProxy对象调用BpBinder对象实现向Java服务对象发起通信请求。
当通过Java层的Service Manager注册Java服务时,在new Java服务时,服务继承自的Java层的Binder类的构造函数中会通过JNI调用初始化接口,初始化接口内部会创建JavaBBinderHolder对象,并将该对象保存到Java层Binder对象的mObject字段中,JavaBBinderHolder对象内部也会持有Java层Binder对象,同时JavaBBinderHolder内部还持有一个mBinder字段用于保存JavaBBinder对象,JavaBBinder内部存在指向Java层Binder对象地址的字段表明其引用了Java层的哪个服务。注册服务的过程中,会获取该JavaBBinder本地对象进行注册,如果获取该JavaBBinder对象的时候该对象还未创建,则会先进行创建,剩下的注册流程即转到C/C++层中的注册流程
当通过Java层的Service Manager获取Java服务时,会从Service Manager中得到Java服务对应的Binder实体对象所对应的Binder引用对象的句柄值,得到该句柄值后,便可创建一个BpBinder对象,进而创建BinderProxy对象,有了BinderProxy对象即可创建Java服务代理对象。获得了Java服务代理对象后,即可通过Java服务代理对象向Java服务发起请求,请求流程最终会交由到C/C++层中的BBinder本地对象与BpBinder对象进行处理。