首先我们知道Android是基于Linux内核的,而Linux内核继承和兼容了丰富的Unix系统进程间通讯(IPC:Internet Process Connection),其中包括:
1、Pipe:管道
2、Signal:信号
3、Trace:跟踪
以上是传统的通讯手段,并且只能用与父进程与子进程之间的通讯,或者兄弟之间的通讯。
4、Message:报文队列
5、Share Memory:共享内存
6、Semaphore:信号量
以上三种是在AT&T的Unix系统V中,又增加了三种称为“SystemV IPC”的进程间通信机制。
7、Socket:插口
其中,Socket是BSD Unix对“System VIPC”机制进行了重要的扩充。
想对以上几种通讯方式详细了解可参考《Linux内核源代码情景分析》一书。
而在Android中采用的是Binder机制实现进程间的通讯。
Binder是一种进程间通信机制,它是一种类似于COM和CORBA分布式组件架构,通俗一点,其实是提供远程过程调用(RPC)功能。使用Client-Server通信方式:一个进程作为Server提供诸如视频/音频解码,视频捕获,地址本查询,网络连接等服务;一个或者多个进程作为Client向Server发起服务请求,获得所需要的服务。
在Android的Binder机制中,包含四个角色,分别是:Server、Client、ServiceManage、Binder驱动。关系如下图:
其中:
Binder驱动:运行于内核空间,Binder机制的核心组件,提供设备文件/dev/binder与用户空间交互。
Server、Client、ServiceManage:运行于用户空间,Service Manager提供了辅助管理的功能,Client和Server在Binder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。
Service Manager和Binder驱动已经在Android平台中实现好,开发者只要按照规范实现自己的Client和Server组件就可以了,具体实现可见下篇:Android系统进程间通讯之Binder机制(二) - 实践篇。
下面就AndroidBinder所会涉及的情景具体分析。
Service Manager是一个linux级的进程,实现对Service的管理。同样的,任何service在被使用之前,均要向Service Manager注册(addService函数实现);同时客户端需要访问某个service时,应该首先向Service Manager查询是否存在该服务,并获取到这个Server(getService函数实现)。
Service Manager的入口函数在service_manager.c中,下面是 Service Manager的代码部分:
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
LOGE("cannot become context manager (%s)/n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
这个进程的主要工作如下:
1.初始化binder,打开/dev/binder设备;在内存中为binder映射128K字节空间;
2.指定Service Manager对应的代理binder的handle为0,当client尝试与Service Manager通信时,需要创建一个handle为0的代理binder,这里的代理binder其实就是第一节中描述的那个代理接口;
3.通知binder driver(BD)使Service Manager成为BD的contextmanager;
4.维护一个死循环,在这个死循环中,不停地去读内核中binder driver,查看是否有可读的内容;即是否有对service的操作要求,如果有,则调用svcmgr_handler回调来处理请求的操作。
5. Service Manager维护了一个svclist列表来存储service的信息。
注意:
1、 当service在向SM注册时,该service就是一个client,而SM则作为了server。而某个进程需要与service通信时,此时这个进程为client,service才作为server。因此service不一定为server,有时它也是作为client存在的。
2、 应用和service之间的通信会涉及到2次binder通信:
a) 应用向SM查询service是否存在,如果存在获得该service的代理binder,此为一次binder通信;
b) 应用通过代理binder调用service的方法,此为第二次binder通信。
首先,Server和Client的实现都是Service Manager实现的,对Server启动而言:Server通过defaultServiceManager函数获得Service Manager远程接口,然后将自己的Service添加到Service Manager中去,接着把自己启动起来,等待Client的请求。下面将通过分析源代码了解Server的实现和启动过程是怎么样的。
假设我们需要定义一个Server:ExampleService。
首先,我们需要定义一个IExampleService(继承了IInterface类),然后定义BnExampleService(继承了IExampleService和BBinder类)。其中BBinder类继承了IBinder类,IInterface和IBinder类又同时继承了RefBase类。
然而BnExampleService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互。IPCThreadState接收到了Client处的请求后,就会调用BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类的onTransact函数,于是,就开始真正地处理Client的请求了。(其中有关IPCThreadState和ProcessState的关系下面会有介绍)。以上便是Server的实现过程。
那么对ExampleService的启动流程呢?
首先:
sp proc(ProcessState::self());
这句代码的作用是通过ProcessState::self()调用创建一个ProcessState实例。ProcessState::self()是ProcessState类的一个静态成员变量,定义在frameworks/base/libs/binder/ProcessState.cpp文件中。
这个函数作用是返回一个全局唯一的ProcessState实例gProcess。全局唯一实例变量gProcess定义在frameworks/base/libs/binder/Static.cpp文件中。
在ProcessState的构造函数中主要以下操作:
1、 open_driver(frameworks/base/libs/binder/ProcessState.cpp)函数打开Binder设备
文件/dev/binder,并将打开设备文件描述符保存在成员变量mDriverFD中:主要是通过open文件操作函数来打开/dev/binder设备文件,然后再调用ioctl文件控制函数来分别执行BINDER_VERSION和BINDER_SET_MAX_THREADS两个命令来和Binder驱动程序进行交互,前者用于获得当前Binder驱动程序的版本号,后者用于通知Binder驱动程序。(open在Binder驱动程序中的具体实现,打开/dev/binder设备文件后,Binder驱动程序就为ExampleService进程创建了一个struct binder_proc结构体实例来维护ExampleService进程上下文相关信息)。
2、 mmap来把设备文件/dev/binder映射到内存中。 mmap函数调用完成之后,Binder驱动程序就为当前进程预留了BINDER_VM_SIZE大小的内存空间了。
至此,ProcessState全局唯一变量gProcess就创建完毕了。
接下来:
调用defaultServiceManager函数来获得Service Manager的远程接口,定义ExampleService::instantiate函数把ExampleService添加到Service Manger中去了,如下:
void ExampleService::instantiate() {
defaultServiceManager()->addService(String16("exampleservice"));
}
其中defaultServiceManager返回的实际是一个BpServiceManger类实例,因此,我们具体了解一下BpServiceManger::addService的实现,这个函数实现在frameworks/base/libs/binder/IServiceManager.cpp文件中。
对addService的理解:BpServiceManger以Client的形式,将相应的一些数据通过Parcel类中各种Parcel.writexxx的方式写入Parcel中,最后通过
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
将data传递,实现信息交互传递(主要是Binder驱动程序之间,传输这个ExampleService)。实现ExampleService的启动过程。
最终实现的就是就是把ExampleService这个Binder实体的引用写到一个struct svcinfo结构体中,主要是它的名称和句柄值,然后插入到链接svclist的头部去。Client来向Service Manager查询服务接口时,只要给定服务名称,Service Manger就可以返回相应的句柄值了,从而实现调用该Service。
最后
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
在一个无穷循环中等待Client的请求。
同样的,在这将简单介绍一下Client的实现流程与获取Server的流程,Client的实现流程原理跟Server如出一辙,获取Server的方式也是通过ServiceManager实现的。
假设我们定义一个Client:ExampleService。
首先我们需要定义一个 IExampleService (继承了IInterface类),然后定义 BpExampleService(继承了IExampleService和Bpinder类)。其中Bpinder类继承了IBinder类,IInterface和IBinder类又同时继承了RefBase类。在BpExampleService调用remote()->transact实现数据传递。
remote()是在BpExampleService的父类BpRefBase中实现的,返回的就是一个BpBinder.实际上调用的就是BpBinder的transact,将写入到Parcel中的数据传递出去并取得返回值。
对于Serve的启动流程主要是调用addService 实现的,对于Client获取Server的流程采用getService实现。
声明:
sp sm = defaultServiceManager();
sp binder;
因为可能存在相对应需要获取的Server可能还没有启动起来,所以这里如果发现取回来的binder接口为NULL,就睡眠0.5秒,然后再尝试获取,这是获取Service接口的标准做法:
do {
binder = sm->getService(String16("exampleservice"));
if (binder != 0) {
break;
}
LOGW("service not published, waiting...");
usleep(500000); // 0.5 s
} while(true);
对于BpServiceManager::getService的具体实现在这就不做详细介绍,跟Server的addService如出一辙。
接着,
sExampleService = interface_cast(binder);
这里的interface_cast实际上最终调用了IExampleService::asInterface函数,创建一个BpExampleService对象供我们得到。有了这个BpExampleService这个远程接口之后,Client就可以调用ExampleService的服务了。
最后,在 BpExampleService 下定义相应的方法实现信息传递 : 定义一个 Parcel data ,将数据写入,然后调用 remote()->transact 函数将数据传递出去,在 Server 下的 onTransact 函数就能接收到相应的 read 出相应的数据。
首先BBinder(也就是BnBinder)与BpBinder均为IBinder的子类,因此可以看出IBinder定义了binder IPC的通信协议,BBinder与BpBinder在这个协议框架内进行的收和发操作,构建了基本的binder IPC机制。
ProcessState是以单例模式设计的。每个进程在使用binder机制通信时,均需要维护
一个ProcessState实例来描述当前进程在binder通信时的binder状态。ProcessState有如下2个主要功能:
1.创建一个thread,该线程负责与内核中的binder模块进行通信,称该线程为Pool thread;(Poolthread:在BinderIPC中,所有进程均会启动一个thread来负责与BD来直接通信,也就是不停的读写BD,这个线程的实现主体是一个IPCThreadState对象. Poolthread的启动方式:ProcessState::self()->startThreadPool();)
2.为指定的handle创建一个BpBinder对象,并管理该进程中所有的BpBinder对象。
IPCThreadState也是以单例模式设计的。由于每个进程只维护了一个ProcessState实例,同时ProcessState只启动一个Pool thread,也就是说每一个进程只会启动一个Pool thread,因此每个进程则只需要一个IPCThreadState即可。
Pool thread的实际内容则为:IPCThreadState::self()->joinThreadPool();
IPCThreadState有两个重要的函数,talkWithDriver函数负责从BD读写数据,executeCommand函数负责解析并执行mIn中的数据。
ProcessState&IPCThreadState&BpBinder&BinderDriver关系图:
Parcel是binderIPC中的最基本的通信单元,它存储C-S间函数调用的参数.但是Parcel只能存储基本的数据类型,如果是复杂的数据类型的话,在存储时,需要将其拆分为基本的数据类型来存储。Parcel类下定义了相应的write和read方法写入和读取数据。
注:写入和读取的顺序需要一样,否则会出错。