http://www.cloudchou.com/android/post-507.html
前一篇博客介绍了Binder IPC程序结构,本篇将从架构角度分析binder, 介绍binder机制的层次划分,并着重分析驱动适配层和Binder核心框架层。
Binder层次划分
Binder层次划分如下图所示:
-
(1) 驱动层
正如大家所知道的,Binder机制是需要Linux内核支持的,Android因此添加了binder驱动,binder 设备节点为/dev/binder,主设备号为10,binder 驱动程序在内核中的头文件和代码路径如下:
kernel/drivers/staging/binder.h
kernel/drivers/staging/binder.c
binder驱动程序的主要作用是完成实际的binder数据传输。
驱动实现时,主要通过binder_ioctl函数与用户空间的进程交换数据(用户进程与驱动交互时使用ioctl函数,对应驱动源码的binder_ioctl函数)。BINDDER_WRITE_READ命令字用来读写数据,数据包中有一个cmd域用于区分不同的请求。binder_thread_write函数用于发送请求,binder_thread_read函数用于读取结果。在binder_thread_write函数中调用binder_transaction函数来转发请求并返回结果。当收到请求时,binder_transaction函数会根据对象的handle找到对象所在的进程,如果handle为0,则认为对象是context_mgr,把请求发给context_mgr所在的进程。所有的binder请求对象全部放到一个RB树中,最后把请求放到目标进程的队列中,等待目标进程读取。
A进程如果要使用B进程的服务,B进程首先要注册此服务,A进程通过service mananger获取该服务的handle,通过这个handle,A进程就可以使用该服务了。A进程使用B进程的服务还意味着二者遵循相同的协议,这个协议反映在代码上就是二者要实现IBinder接口。
Binder的本质就是要把对象a从一个进程B映射到另一个进程A中,进程A中调用对象a的方法象调本地方法一样。但实际上进程B和进程A有不同的地址空间,对象a只有在进程B里有意义,但是驱动层可将进程B的对象a映射到进程A,得到对象a在进程A的表示,称之为handle,也叫句柄。这样,对象a在进程B的地址空间里有一个实际地址,在进程A里有对应的句柄,驱动会将这个句柄和对象a的实际地址映射起来。对象a对于进程B来说是本地对象,对象a对于进程A来说是远程对象,而handle对于进程A来说是对象a在进程A的引用。
适配层使用binder驱动时使用了内存映射技术,故此进程间传输数据时只需拷贝一次,传统的IPC需拷贝两次,因此使用binder可大大提高IPC通信效率。
-
(2) 驱动适配层
主要是IPCThreadState.cpp和ProcessState.cpp,源码位于frameworks/native/libs/binder
这两个类都采用了单例模式,主要负责和驱动直接交互。
ProcessState负责打开binder设备,进行一些初始化设置并做内存映射
IPCThreadState负责直接和binder设备通信,使用ioctl读写binder驱动数据
后面将详细分析ProcessState和IPCThreadState。
-
(3) Binder核心框架层
Binder核心框架主要是IBinder及它的两个子类,即BBinder和BpBinder,分别代表了最基本的服务端及客户端。
binder service服务端实体类会继承BnInterface,而BnInterface会继承自BBinder,服务端可将BBinder对象注册到servicemananger进程。
客户端程序和驱动交互时只能得到远程对象的句柄handle,它可以调用调用ProcessState的getStrongProxyForHandle函数,利用句柄handle建立BpBinder对象,然后将它转为IBinder指针返回给调用者。这样客户端每次调用IBinder指针的transact方法,其实是执行BpBinder的transact方法。
-
(4) Binder框架层
本地Binder框架层包含以下类(frameworks/native/libs/binder):
RefBase,IInterface,BnInterface,BpInterface,BpRefBase,Parcel 等等
Java框架层包含以下类(frameworks/base/core/java/android/os):
IBinder,Binder,IInterface,ServiceManagerNative,ServiceManager,BinderInternal,IServiceManager,ServiceManagerProxy
Java框架层的类的部分方法的实现在本地代码里(frameworks/base/core/jni)。
后续博客会详细分析本地binder框架和Java层 binder框架各自的类关系。
-
(5) Binder 服务和客户端实现
从Binder入门系列我们也知道,binder service服务端和binder 客户端都有native和Java之分,Java层服务端从Binder继承并实现服务接口,Java层客户端直接实现服务接口即可,而本地服务端需继承自BnInterface,本地客户端继承自BpInterface。
后续博客分析本地binder框架和Java层 binder框架时会给出更详尽的类关系。
ProcessState
ProcessState负责打开binder设备,进行一些初始化设置。
ProcessState的类图如下图所示:
我们通常在binder service的服务端象下面一样使用ProcessState:
注意binder service服务端提供binder service时,是以线程池的形式提供服务,也就说可以同时启动多个线程来提供服务,可以通过如下方式来启动多个线程:
接下来我们分析ProcessState的构造函数:
open_driver的实现:
startThreadPool的实现(用于启动线程池):
spawnPooledThread的实现:
PoolThread继承自Thread类,Thread类是框架层提供的一个类,和Java的Thread类相似,使用PoolThread类时需实现threadLoop函数(Java使用Thread类时需覆盖run方法),新线程执行threadLoop函数,启动新线程需调用PoolThread对象的run方法(Java中调用start方法)。
PoolThread类的源码如下所示:
IPCThreadState
IPCThreadState负责直接和binder设备通信,从binder驱动读取数据,并向binder驱动写数据。
IPCThreadState也采用了单例模式。
IPCThreadState类图如下图所示:
我们通常在binder service的服务端象下面一样使用IPCThreadState:
IPCThreadState::self()->joinThreadPool();
IPCThreadState joinThreadPool的函数原型:
void joinThreadPool(bool isMain = true);
IPCThreadState的构造函数实现:
joinThreadPool的函数实现如下所示:
talkWithDriver的实现:
executeCommand的实现:
Binder核心框架层
Binder核心框架主要是IBinder及它的两个子类,即BBinder和BpBinder,分别代表了最基本的服务端及客户端。类图如下图所示:
-
1) IBinder(frameworks/native/include/binder/IBinder.h)
IBinder有两个直接子类,代表服务端的BBinder和代表客户端的BpBinder。
重要方法说明(以下方法均是抽象方法):
queryLocalInterface 用于检查IBinder是否实现了descriptor指定的接口,若实现了则会返回它的指针
getInterfaceDescriptor 用于返回IBinder提供的接口名字
transact 客户端用该方法向服务端提交数据,服务端实现该方法以处理客户端请求。
-
2) BBinder(frameworks/native/include/binder/Binder.h)
BBinder代表binder service服务端,最终声明的binder 服务类将间接从该类继承,它实现了IBinder声明的transact方法,转调留给子类实现onTrasact的方法。
-
3) BpBinder(frameworks/native/include/binder/BpBinder.h)
客户端使用servicemananger查询服务时实际上是先得到一个句柄handle,然后ProcessState的getStrongProxyForHandle函数里利用句柄handle建立BpBinder对象,再转为IBinder指针,代理类便是通过该指针向服务端请求。
参考资料
书《Android系统原理及开发要点详解》 第4章Android的底层库和程序
书《Android技术内幕 系统卷》 第3章 Android的IPC机制―Binder