Android Binder机制
备注:
文章大部分内容路源于:
https://blog.csdn.net/crs0313/article/details/81737323?spm=1001.2014.3001.5501
https://blog.csdn.net/crs0313/article/details/81737408?spm=1001.2014.3001.5501
主要记录学习的过程,可能会有错误的地方
IPC
所谓IPC,IPC(Inter-Process Communication)进程间通信,就是跨进程通信。线程是CPU调度的基本单位,而进程则是向系统申请资源的基本单位。同一个进程中的各个线程是可以相互访问内存的,因为这些线程中的变量都是在堆栈中的。
Linux C编程中有几种方法
(1) 半双工Unix管道
(2) FIFOs(命名管道)
(3) 消息队列
(4) 信号量
(5) 共享内存
(6) Socket
为什么选择Binder
Android是基于Linux内核的,Binder是Android基于Linux的一种独特的IPC机制
一者性能方面,传输效率问题,传统的管道队列模式采用内存缓冲区的方式,数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程,而socket都知道传输效率低,开销大,用于跨网络进程交互比较多,共享内存虽然无需拷贝。
二者这是安全问题,Android作为一个开放式,拥有众多开发者的的平台,应用程序的来源广泛,确保终端安全是非常重要的,传统的IPC通信方式没有任何措施,基本依靠上层协议,其一无法确认对方可靠的身份,Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志,传统的IPC要发送类似的UID也只能放在数据包里,但也容易被拦截,恶意进攻,socket则需要暴露自己的ip和端口,知道这些恶意程序则可以进行任意接入。
三者是Binder本身是C/S架构的,易用性高。
Binder只需要一次拷贝,性能仅次于共享内存,而且采用的传统的C/S结构,稳定性也是没得说,发送添加UID/PID,安全性高。
image-20210522184445024整体架构
img图片来源:https://blog.csdn.net/dfvbrtdhy/article/details/113571419?spm=1001.2014.3001.5501
Binder的实现分为这么几层
-
Java应用层部分:定义了Binder FrameWork层通信的接口
-
Framework层:定义了和Binder驱动的通信的规则,C/S架构
-
驱动层:数据传递,对象标识,线程管理,调用过程控制等功能
Binder模型的4类角色:Binder驱动,ServiceManager,Server和Client
驱动层
源码路径
/kernel/drivers/android/binder.c
/kernel/drivers/android/binder_internal.h
/kernel/include/uapi/linux/android/binder.h
binder.c 的主要工作
image-20210522191847247 在这里插入图片描述图片来源:https://blog.csdn.net/dfvbrtdhy/article/details/113571419?spm=1001.2014.3001.5501
mmap
mmap的作用是进行内存映射。当应用调用mmap()映射内存到进程虚拟地址时,该函数会进行两个操作:第一,将指定大小的”物理内存” 映射到 “用户空间”(即,进程的虚拟地址中)。 第二,将该”物理内存” 也映射到 “内核空间(即,内核的虚拟地址中)”。
简单来说,就是”将进程虚拟地址空间和内核虚拟地址空间映射同一个物理页面”。
在Binder通信机制中,mmap()会将Server进程的虚拟地址和内核虚拟地址映射到同一个物理页面。那么当Client进程向Server进程发送请求时,只需要将Client的数据拷贝到内核空间即可!由于Server进程的地址和内核空间映射到同一个物理页面,因此,Client中的数据拷贝到内核空间时,也就相当于拷贝到了Server进程中。因此,Binder通信机制中,数据传输时,只需要1次内存拷贝!
Binder中的数据结构
内核空间的Binder数据结构
详细信息请参考:Android Binder机制(二) Binder中的数据结构
binder_proc:是描述进程上下文信息的,每一个用户空间的进程都对应一个binder_proc结构体Binder驱动的文件节点是"/dev/binder",每当一个程序打开该文件节点时;Binder驱动中都会新建一个binder_proc对象来保存该进程的上下文信息。
binder_node:是Binder实体对应的结构体,它是Server在Binder驱动中的体现
binder_ref:是Binder引用对应的结构体,它是Client在Binder驱动中的体现
binder_buffer:binder_buffer是描述Binder进程所管理的每段内存的结构体。
binder_thread:描述Binder线程的结构体
flat_binder_object:描述Binder对象信息的结构体
binder_write_read:描述Binder读写信息的结构体。
binder_transaction_data:描述Binder事务交互的数据格式的结构体
用户空间的Binder数据结构
ServiceManager守护进程中的数据结构
binder_state:定义在frameworks/native/cmds/servicemanager/binder.c中,它是ServiceManager用来描述打开的"/dev/binder"的信息结构体
binder_object:定义在frameworks/native/cmds/servicemanager/binder.h中,ServiceManager中与flat_binder_object对应的结构体
binder_txn:定义在frameworks/native/cmds/servicemanager/binder.h中,ServiceManager中与binder_transaction_data对应的结构体
svcinfo:定义在frameworks/native/cmds/servicemanager/service_manager.c中,它是ServiceManager守护进程的私有结构体,svcinfo是保存"注册到ServiceManager中的服务"的相关信息的结构体。它是一个单链表,在ServiceManager守护进程中的svclist是保存注册到ServiceManager中的服务的链表,它就是struct info类型。svcinfo中的next是指向下一个服务的节点,而ptr是该服务在Binder驱动中Binder引用的描述。name则是服务的名称。
C++层的数据结构
Parcel:Parcel是描述Binder通信信息的结构体
ServiceManager
ServiceManager是用户空间的一个守护进程,它一直运行在后台。它的职责是管理Binder机制中的各个Server。当Server启动时,Server会将”Server对象的名字”连同”Server对象的信息”一起注册到ServiceManager中;而当Client需要获取Server接入点时,则通过”Server的名字”来从ServiceManager中找到对应的Server。
ServiceManager是整个Binder机制能够运作的的桥梁和管家,ServiceManager管理了Android的系统服务和用户的服务。
img图片来源:https://blog.csdn.net/dfvbrtdhy/article/details/113571419?spm=1001.2014.3001.5501
Android系统启动时会开启init进程,init进程会先去创建servicemanager进程,其所对应的可执行程序servicemanager,所对应的源文件是service_manager.c,进程名为servicemanager。
启动ServiceManager的入口函数是 service_manager.c 中的main()方法,主要做了以下操作
binder_open
ServiceManager进程:打开/dev/binder,同时映射物理内存到进程空间。
Binder驱动:新建并初始化该进程对应的binder_proc结构体,同时将内核虚拟地址和该进程的虚拟地址映射到同一物理内存中。
binder_become_context_manager
ServiceManager进程:告诉Kernel驱动,当前进程(即ServiceManager进程)是Binder上下文管理者。
Binder驱动:新建当前线程对应的binder_thread对象,并将其添加到进程上下文信息binder_proc的threads红黑树中;新建ServiceManager对应的binder实体,并将该binder实体保存到全局变量binder_context_mgr_node中。
binder_loop
ServiceManager进程:binder_loop()通过BC_ENTER_LOOPER告诉Kernel,ServiceManager进入了消息循环状态。接着,ServiceManager就进入等待状态,等待Client请求。
Binder驱动:已知ServiceManager进入了消息循环状态;在收到ServiceManager的BINDER_WRITE_READ消息之后,就去ServiceManager的从进程上下文binder_proc对象中读取是否有待处理事务,由于没有事务处理,则将ServiceManager线程设为中断等待状态。
ServiceManager的main()进程完成了以下工作
对于ServiceManager进程而言
它打开了Binder设备文件,并且将内存映射到ServiceManager的进程空间。然后,它告诉Binder驱动自己是Binder上下文的管理者。最后,进入消息循环,等待Client请求。
对于Binder驱动而言
初始化了ServiceManager对应的进程上下文环境(即binder_proc变量),并将内核虚拟地址和进程虚拟地址映射到同一物理内存中。然后,新建当前线程对应的binder_thread对象,并将其添加到进程上下文信息binder_proc->threads红黑树中。在得知ServiceManager是Binder上下文管理者后,建立ServiceManager对应的Binder实体,并将该Binder实体保存到全局变量中。最后,得知ServiceManager进入消息循环后,由于当前线程中没有事务可处理,则进入中断等待状态,等待其他进程将其唤醒。
Service Manager的获取
获取Service Manager是通过defaultServiceManager()方法来完成。
frameworks/native/libs/binder/IServiceManager.cpp
sp<IServiceManager> defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
while (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
if (gDefaultServiceManager == NULL)
sleep(1);
}
}
return gDefaultServiceManager;
}
ProcessState::self()->getContextObject(NULL)
会先通过open_driver()打开”/dev/binder”,设置线程最大数目:15, 设置共享内存大小 (1M-8K),接着调用mmap()映射内存到当前进程中。此时,ProcessState就初始化完毕,它将”/dev/binder”的文件句柄以及映射内存都保存在自己的私有成员中。
getContextObject()
获取ServiceManager对应的BpBinder代理对象。 在新建BpBinder时,会通过IPCThreadState::self()获取IPCThreadState对象;因为,需要通过IPCThreadState对象来与Binder驱动进行交互。
interface_cast
前面已经成功获取到了ServiceManager的BpBinder代理,而defaultServiceManager()返回的是IServiceManager对象。这里,使用了一个技巧,通过宏interface_cast而调用asInterface()函数,从而返回IServiceManager的代理BpServiceManager。这样,defaultServiceManager()就执行完毕了。
主要类
RefBase
它定义在system/core/include/utils/RefBase.h中。RefBase是一个公共父类,它声明了许多常用的接口。包括增加引用计数,获取引用计数,新增对象的弱引用等接口。
IInterface
它定义在frameworks/native/include/binder/IInterface.h中。和RefBase类似,它也是一个公共父类,IInterface中声明了asBinder()方法,用于获取对象的IBinder对象。
IBinder
它定义在frameworks/native/include/binder/IBinder.h中。IBinder也是一个抽象出来的类,它包括了localBinder(), remoteBinder()和transact()等非常重要的接口。IBinder有两个直接子类类:BpBinder和BBinder。
BpBinder
BpBinder是Binder代理类。通过remoteBinder()可以获取BpBinder对象;而且,对于C++层而言,它相当于一个远程Binder。BpBinder的事务接口transact()会调用IPCThreadState的transact(),进而实现与Binder驱动的事务交互。此外,BpBinder中有一个mHandle句柄成员,它用来保存Server位于Binder驱动中的”Binder引用的描述”。句柄0是ServiceManager的句柄。
BBinder
BBinder是本地Binder。通过localBinder()可以获取BBinder对象。当Server收到请求之后,会调用BBinder的onTransact()函数进行处理。而不同的Server会重载onTransact()函数,从而可以根据各自的情况对事务进行处理。
BpInterface
它定义在frameworks/native/include/binder/IInterface.h中。实际上,BpInterface是一个模板类,同时继承了BpRefBase和INTERFACE,这里的INTERFACE是模板。像IServiceManager,IMediaPlayerService等Server都是通过继承模板类是实现的。
BnInterface
它定义在frameworks/native/include/binder/IInterface.h中。和BpInterface类似,BnInterface也是一个模板类,它同时继承了BBinder和INTERFACE。像BnServiceManager,BnMediaPlayerService等本地Server都是通过继承模板类是实现的。
BpRefBase
它定义在frameworks/native/include/binder/Binder.h中。BpRefBase继承于RefBase,它有一个IBinder*类型的成员mRemote,同时提供了获取该mRemote的方法。实际上,该mRemote就是BpBinder对象。
ProcessState
它定义在frameworks/native/libs/binder/ProcessState.cpp中中。ProcessState的实例是采用单例模式实现的,它拥有两个非常重要的成员:mDriverFD和mHandleToObject。
mDriverFD是文件”/dev/binder”的句柄,而mHandleToObject是一个Vector矢量数组,矢量数组中的每个元素都保存了两个变量:Server的句柄,以及Server对应的BpBinder对象。实际上,Server的句柄是”Server在Binder驱动中的Binder引用的描述”;句柄0是ServiceManager的句柄。
IPCThreadState
它定义在frameworks/native/libs/binder/IPCThreadState.cpp中中。IPCThreadState的实例也是采用单例模式实现的,它是正在与Binder驱动进行交互的类。
对于一个Server而言,它都会存在一个”远程BpBinder对象”和”本地BBinder对象”。
- 远程BpBinder对象的作用是和Binder驱动进行交互。具体的方式是,当Server要向Binder发起事务请求时,会调用BpBinder的transact()接口,而该接口会调用到IPCThreadState::transact()接口,通过IPCThreadState类来和Binder驱动交互。此外,该BpBinder在Binder驱动中的Binder引用的描述会被保存到ProcessState的mHandleToObject矢量缓冲数组中。
- 本地BBinder对象的作用,是Server响应Client请求的类。当Client有请求发送给Server时,都会调用到BBinder的onTransact()函数,而每个Server都会覆盖onTransact()函数。这样,每个Server就可以在onTransact()中根据自己的情况对请求进行处理。
gDefaultServiceManager实际返回的是一个BpServiceManager对象,该对象包含IBinder的代理BpBinder,和Binder驱动进行交互。
注册Server到ServiceManager中
客户端:
getIServiceManager().addService(name, service, false)
-
ServiceManagerNative.asInterface(BinderInternal.getContextObject())
- BinderInternal.getContextObject(): new ServiceManagerProxy(new BinderProxy()),创建一个BpBinder并且和BinderProxy 绑定,并返回BinderProxy 对象
- ServiceManagerNative.asInterface:返回 ServiceManagerProxy,此时ServiceManagerProxy持有BinderProxy
-
addService
- 将服务service 放入 data中
- mRemote.transact 相当于BinderProxy的transact ,调用writeTransactionData给驱动写入BC_TRANSACTION
- waitForResponse:数据拷贝数据到驱动,就也是映射到了一个物理空间,然后记录客户端的写入命令BR_TRANSACTION_COMPLETE,客户端挂起,发送BINDER_WORK_TRANSACTION给ServiceManager,调用wake_up_interruptible 唤醒ServiceManager
ServiceManager
- 处理客户端的BINDER_WORK_TRANSACTION ,然后给服务端写入BR_TRANSACTION命令
- 用 svclist 保存所有服务的。接着记录服务端的命令为BC_REPLAY,记录任务,然后写入ServiceManager的命令为BINDER_WORK_TRANSACTION_COMPLETE,ServiceManager挂起,调用wake_up_interruptible(target_wait)唤醒客户端
客户端被唤醒
- 处理BINDER_WORK_TRANSACTION 命令,然后写入BR_REPLY给驱动
Aidl工作过程
AIDL文件会在开发工具在编译的时候生成一个文件,那么这个文件就是Binder通信在应用层的实现。我们具体梳理流程如下:
生成的文件类解析:
Sub抽象类继承Binder,空实现通信接口,并持有服务的Token,重载onTransact方法处理客户端传入的数据,并且给reply赋值
通信接口的实现类也就是代理类Proxy,在构造中接受一个IBInder对象即remote,在方法实现中打包通信的数据,使用remote的transact方法提交数据
transact之后,一般都是同步将会挂起
通信过程:
-
服务端定义一个Server,在服务绑定成功后,onBind回调中返回一个继承自Binder的Sub实例
-
客户端绑定服务成功的时候创建Proxy实例,并将服务下发的IBinder实例给了Proxy,客户端使用Proxy调用接口,进入对应的方法,实现数据的封装打包,然后使用IBinder实例调用transact
-
之后进入native和kernel进行内存映射
-
之后ServiceManager会调用Server服务端的onTransact方法,就是继承Binder的Sub类中,在onTransact中接受到客户端传入的数据,然后将数据下发到具体的实现类,也就是Server中的Sub的实现类中
参考文章:
https://segmentfault.com/a/1190000018317841?utm_source=tag-newest
https://blog.csdn.net/crs0313/article/details/81737242?spm=1001.2014.3001.5501
https://blog.csdn.net/crs0313/article/details/81737323?spm=1001.2014.3001.5501
https://blog.csdn.net/crs0313/article/details/81737408?spm=1001.2014.3001.5501
https://blog.csdn.net/crs0313/article/details/81737478?spm=1001.2014.3001.5501
网友评论