Android Binder机制解析
概述
Binder是Android系统中提供的一种IPC(进程间通信)机制。Android系统
是基于Linux内核而开发的,除了Binder之外,它还存在其他的IPC机制,如Pipe和Socket。所谓进程间通信,其实是一组编程接口,它能让程序员协调不同进程,使之在同一个操作系统中同时运行并进行信息的传递和交换。Binder机制在Android系统中具有重要的作用,Android的C/S架构,基本上都是基于Binder实现的,破解Binder机制,对破解Android源码,提升代码理解力具有重要意义。
图1.1 IPC进程间通信示意图
所谓C/S架构,顾名思义,从角色的角度上看会存在Client端和Server端,但在Android系统中,还存在一个统领全局的ServiceManager,它主要的作用是管理系统中各种服务,如CameraServer、MediaServer和ImageEffectService等。
图1.2 Android系统C/S结构意图
- 图1.2中,任意两者间的通信都是进程间通信,并且是通过Binder实现的
- Server端作为一个服务,在启动时,需要将自己注册到ServiceManager中,故而它是ServicManager的一个客户端。
- Client端想要使用某个服务也必须先从ServiceManager中获取该服务的信息,故而Client也是ServiceManager的客户端。
- Client根据向ServiceManager获取到的信息,与Server建立通信通路,然后直接与Server进行交互,故而Client又是Server的客户端。
- 一个进程,既可以是客户端,也可以是服务端。
本节从概念和结构上简述了进程间通信和Android所采用的C/S架构角色之间的关系。Binder正是各种角色之间的沟通者、通信员。在Binder程序设计中,无论是客户端还是服务端,如果它想进行进程间通信,就必须建立Binder通信。但最重要的,还是各自业务本身的建设和完善。虽然Binder通信的建立至关重要,但它也仅仅是一个通信的建立而已,在看代码时,应保持清醒的头脑,将业务函数与通信建立函数区分开来,这样才能更迅速的找到自己感兴趣的代码,而不至于陷入“人民战争(代码)的汪洋大海”。下一章节将会以MediaServer为切入点,从代码角度对Binder机制进行解析。
MediaServer入口函数
CameraServer的入口函数如下所示,Linux可执行程序的入口函数就是一个main函数,不难看出,CameraServer就是在本进程中被创建。
int main(int argc __unused, char **argv __unused)
{
signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGI("ServiceManager: %p", sm.get());
InitializeIcuOrDie();
MediaPlayerService::instantiate();
ResourceManagerService::instantiate();
registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
- ProcessState类的分析
ProcessState采用的是单例模式,所谓单例,就是指在当前进程中只有一个
ProcessState对象的实例。分析其构造函数,第一句话便调用了open_driver();用于打开/dev/binder这个设备,它是Linux中为完成进程间通信而专门设置的虚拟设备。在获取到binder设备文件描述符后,调用mmap为它分配一块内存来接收数据。
ProcessState::ProcessState()
: mDriverFD(open_driver())
, mVMStart(MAP_FAILED)
......
,
{
if (mDriverFD >= 0) {
//为打开的文件描述符map一块内存
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
close(mDriverFD);
mDriverFD = -1;
}
}
}
分析open_driver()函数,通过open系统调用,打开了/dev/