Android Binder机制

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驱动ServiceManagerServerClient

驱动层

源码路径

/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 defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast(
                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

你可能感兴趣的:(Android Binder机制)