背景
Binder
是Android进程间的一种通信方式,属于C/S
架构。 Android系统的底层基于Linux内核。相对与传统的IPC方式,Binder在效率上
有显著的优势。通过Binder来实现进程间通信本质上是通过Binder驱动来完成的。
Binder Driver
将自己注册为一个misc device
(杂项设备),并向上层提供驱动节点:dev/binder。 这种"杂项"设备不是真正的物理
硬件设备,而是虚拟驱动。它运行在内核态中,提供open()、mmap()、ioctl()
等常用操作。
Binder
通信涉及到四个部分:
- Server: 提供接口服务的具体实现;向
ServiceManager(SM)
注册服务。 - Client: 从SM获取服务,调用服务提供的接口。
- ServiceManager: Binder大管家。管理binder相关的服务。提供注册、查询服务等接口
- Binder驱动: 本质上实现跨进程通信。
一、Android为什么要采用Binder机制
Linux本身提供了多种多进程的通信方式: 管道、信号量、队列、socket、共享内存
- 管道:只能单向通信。 进程A到管道一次拷贝,管道到进程B一次拷贝,总共两次拷贝(用户->内核->用户)。
- 信号量:一般作为进程间的同步锁来使用。
- 信号:一般是用来内核给应用发送某个信号通知,如非法访问某个内存地址、杀死进程信号。
- 队列:进程A到队列一次拷贝,队列到进程B一次拷贝,总共两次拷贝(用户->内核->用户)。
- socket: 通信效率较低,开销大。分为本机基于fd的socket低速通信,和基于网络的tcp/udp的socket。需要两次拷贝。
- 共享内存:让两个进程映射到同一个内存缓冲区,实现共享,无需拷贝。但是需要自己维护同步过程,复杂且难用。
- Binder:通过mmap映射机制,让应用进程可以直接访问内核内存,减少了一次拷贝。效率仅次于共享内存。
二、C/S 角度
Binder通信过程中涉及到了多个C/S场景:
- 一般情况下是client作为客户端,server作为服务端。
- 当注册服务时,Server作为client端,SM作为server端。
- 当获取服务时,client作为客户端,SM作为服务端。
三、分层角度
3.1 java层
涉及到的类:
假设定义一个IHelloService.aidl
文件,经过编译会生成IHelloService.java
文件。
通信过程如下:
- Server:
HelloService
继承内部抽象类Stub
,实现接口sayHello()
。- 调用
ServiceManager.addService
方法,注册服务到ServiceManager
- Client:
- client通过
ServiceManager
获取到Server的本IBinder对象 - 通过调用
IHelloService.asInterface(IBinder obj)
转换成一个IHelloService
本地对象。 - 调用接口
sayHello()
完成跟服务端通信
client和server整个过程涉及到类图 :
重点:
Proxy中的mRemote
其实是一个BinderProxy
对象。BinderProxy的mNativeData
指向的是一个c++ 层的Binder对象,实际上一个BpBinder
。
ServiceManager类相关的类图:
重点:
ServiceManagerProxy
中的mRemote
是一个binderProxy对象。BinderProxy的mNativeData
指向的是一个c++ 层的Binder对象,实际上一个BpBinder
。
3.2 c++层
java层把数据封装好后,通过BinderProxy中的native
指针,把数据传到了c++层。 从client客户端角度出发,会有BpBinder对象来完成接下来的操作。
总体上来讲就算没有java层,c++层完全可以实现客户端和服务端通过Binder驱动来完成通信。因此,进程通信的核心逻辑应该在c++层。里面肯定会涉及到ioctl()、open()、mmap()
等系统调用的操作。
ServiceManager类图如下:
BpRefBase
类里面有一个IBinder *指针
类型的成员变量 mRemote
。
mRemote指向了一个BpBinder对象, BpBinder
里面含有一个mHandle
成员变量。
HelloService相关类图:
3.3 内核驱动层
这个已经很清晰表明了内核中发生的事情。Binder驱动作为一个misc
“杂项驱动”,它会在系统初始化阶段把binder_open()、binder_ioctl()、binder_mmap()
等函数完成注册。当用户态调用open进入到内核中,内核会直接调用之前注册好的驱动函数。
在内核态,对于每一个进程都会创建一个binder_proc
与之对应。binder_proc
中会有binder_buffer
。这一块buffer
缓冲区就是通过mmap()
映射而来,因此,只要Client把数据copy_from_user()
到buffer
,然后通知server直接读取buffer
中的数据即可。
最后
Binder机制非常的复杂,以上只是简单的架构的角度来大体熟悉了binder机制分层思想,如有错误之处还请指正。后续将对每个层的细节进行解析。
参考: