Android binder 驱动原理与机制分析

Android binder 驱动原理与机制分析

binder 是通过Linux的 Binder Driver 来实现的,binder 操作类似于线程迁移,两个进程间通信看起来像是一个进程进入另一个进程去执行代码,然后带着执行结果返回。

binder 驱动原理及实现
为了完成进程间通信,binder 采用了AID(Android interface definition language)来描述进程间的接口,
在实际的实现中,binder 是作为一个特殊的字符型设备而存在的,设备节点为 /dev/binder, 其实现遵循linux设备驱动模型;
binder 的实质就是要把对象从一个进程映射到另一个进程中,而不管这个对象是本地的还是远程的;
本地对象表示本地进程的地址空间的一个地址,而远程对象的引用则是一个抽象的32位句柄;所有的远程进程的对象的引用都是一个句柄;
binder 驱动负责两种不同名称的对象的正确映射,这样才能把数据发送给正确的进程进行通信。这个映射关系也是进程间引用对象的基础,
对一个对象的引用,在远程是句柄,在本地则是地址(即本地对象的地址)。
我们把进程之间传递的数据称之为 binder 对象,在对应源码中使用 flat_binder_object 结构体 来表示;传输的数据是一个复用数据联合体。
对于 binder 类型,数据就是一个 binder 本地对象,handle类型就是一个远程的handle句柄。
binder 对象所传递的实际内容是通过另外一个结构体 binder_transaction_data 来表示的,其中的target字段又是一个复合联合体对象,
target字段中的handle是要处理此事件的目标对象的句柄,根据此handle,binder 驱动可以找到应该由哪个进程处理此事件,并且把此事件的任务分发给一个线程,
而那个线程也正在执行 ioctl 的 BINDER_WRITE_READ 操作,即正在等待一个请求。
当 binder 驱动找到处理对应事件的进程后,binder 驱动就会把需要处理的事件的任务放在读缓冲(binder_write_read)里,给这个服务线程,
该服务线程则会执行指定命令的操作;处理请求的线程把数据交给合适的对象来执行预定操作,然后把返回结果同样用 binder_transaction_data 结构封装,
以写命令的方式传回给 binder 驱动,并将此数据放在一个读缓冲(binder_write_read)里,返回给正在等待结果的原进程(线程),这样就完成了一次通信。


binder 的架构和实现
Android对 binder 驱动所提供的接口进行了封装,同时还在Android的工具库中提供了一套 binder 库;
在Android的设计中,每个activity都是一个独立的进程,每个service也是一个独立的进程,可以把activity看做一个客户端,把service看做一个服务端,
实际上也就是一个客户端和服务端之间的通信,具体的通信流程则由 binder 来完成;

binder 机制的组成
Android的 binder 机制就是一个C/S架构,客户端和服务端直接通过 binder 交互数据,打开 binder 写入数据,通过 binder 读取数据,这样通讯就完成了。
数据的读写由 binder 驱动完成,除了 binder 驱动,整个机制还包括以下几个组成部分:
1. Service Manager
主要负责管理Android系统中所有的服务,当客户端需要与服务端进行通信时,首先会通过Service Manager来查询和取得所需要交互的服务。
每个服务也都需要向Service Manager注册自己提供的服务,以便能够共客户端进行查询和获取。
2. 服务 Service
这里的服务之上面所述的服务端,通常也是Android的系统服务,通过Service Manager可以查询和获取某个Service。
3. 客户端
这里的客户端一般是指Android系统上面的应用程序,比如Activity,它可以请求Service中的服务。
4. 服务代理
服务代理是指在客户端应用程序中生成的Server代理(proxy)。
从应用程序的角度来看,代理对象和本地对象没有差别,都可以调用其方法,方法都是同步的,并且返回相应的结果。

binder 的系统架构
在Android的源码中,有关 binder 的实现在各层都有,主要的 binder 库由本地原生代码实现,
Java和C++层都定义有同样功能的 binder 接口,供应用程序使用,他们实际上都是调用原生 binder 库的实现。

binder 驱动主要负责组织 binder 的服务节点,调用 binder 相关的处理线程,完成实际的 binder 传输等,它位于 binder 结构的最底层(即linux内核层)。
binder adapter层是对 binder 驱动的封装,主要用于操作 binder 驱动,即应用程序不必直接接触 binder 驱动程序。
binder 核心库是 binder 框架的核心实现,主要包括IBinder,Binder(服务器端)和BpBinder(客户端);
位于最上面两层的 binder 框架和具体的客户端、服务端都分别有Java和C++两种实现方案,主要供应用程序(比如摄像头和多媒体等)使用,他们通过调用 binder 的核心库来实现。


binder 的机制和原理
binder 的工作流程
1. 客户端首先获得服务器端的代理对象。所谓的代理对象实际上就是在客户端建立一个服务端的引用,该代理对象具有服务端的功能,使其在客户端访问服务端的方法就像访问本地方法一样。
2. 客户端通过调用服务器代理对象的方式向服务器端发送请求。
3. 代理对象将用户请求通过 binder 驱动发送到服务器进程。
4. 服务器进程处理用户请求,并通过 binder 驱动返回处理结果给客户端的服务器代理对象。
5. 客户端收到服务器端的返回结果。
结果这样一个流程,就完成了一次 binder 通信。

1. 服务
服务的本质就是响应客户端的请求。要提供服务,就必须建立接收请求,处理请求,应答客户端的框架。因此任何一个服务都必然存在一个循环监听、处理请求的过程。
在Android中一共包括3种服务,分别是Native Service,Android Service,Init Service;
Native Service实际上就是完全在C++空间完成的服务,主要是指系统一开始初始化时通过init.rc脚本启动的服务,例如Service Manager Service,Zygote service,Media Service,ril_demon_service等。
Android服务是指在JVM空间完成的服务,虽然也要使用Native上的框架,但是服务主体位于Android空间中。Android服务是第二阶段初始化时建立的服务。
init空间服务主要用于完成属性设置,其通信采用socket方式。
不管是Native Service还是Android Service,都要用Android中的 binder 驱动来完成与客户端之间的通信。

2. Service Manager
从名字可以看出Service Manager是所有服务的管理器,因此所有Server(System Server)都需要向它注册,应用程序需要向其查询相应的服务。
Service Manager本身就是一个服务,只是它比较特殊,会管理其他所有的服务。

binder 机制实际上就是一个类似于C/S的架构,客户端进程想要与服务端进程通信,就必须在客户端建立一个服务端进程代理对象,然后将请求发送带代理对象上;
代理对象通过 binder 驱动将请求转发给服务端进程处理;当处理完成之后,再次通过 binder 驱动传回给代理对象,客户端从代理对象获取相应信息。

3. binder 的机制
Android对 binder 机制进行了抽象,定义了IBindre接口,该接口是对跨进程对象的抽象,在C/C++和Java层都有定义。
一个普通对象只能在当前进程中被访问,如果希望它能够被其他进程访问,就必须实现 Ibinder 接口。
IBinder 接口可以指向本地对象,也可以指向远程对象,关键就在于 IBinder 接口中的transact函数。
如果 IBinder 指向的是一个服务端代理,那么transact只负责把请求发送给服务器;
如果 IBinder 指向的是一个服务端,那么transact函数只负责提供服务即可。
因此不管是服务端还是服务端代理对象,都必须实现该接口,才能进行binder 通信。
注意:transact方法是同步方法,将会挂起客户进程的当前线程,直到Service把请求处理完成并返回结果。

用法:
Service端,首先调用defaultServiceManager函数获取BpServiceManager对象,可以通过binder 与ServiceManager交互,然后通过BpBpServiceManager对象的addService()方法注册BnService服务到ServiceManager。
客户端,首先通过defaultServiceManager函数获取BpServiceManager对象,然后调用ServiceManager::getService查询指定名称的Service服务,再通过interface_cast将查询得到的Binder 转化为BpService对象。

Binder机制及底层实现
进程空间分配

进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
进程间,内核空间的数据可共享,所以内核空间 = 可共享空间
进程内用户与内核进行交互称为系统调用

Binder跨进程通信(IPC)的原理
先通过进程间的内核空间进行数据交互
再通过进程内的用户空间&内核空间进行数据交互,从而实现进程间的用户空间的数据交互
而Binder,就是充当连接两个进程(内核空间)的通道

使用步骤:
注册服务
* Server进程向Binder驱动发起服务注册请求
* Binder驱动将注册请求转发给ServiceManager进程
* ServiceManager进程添加该服务
* 此时ServiceManager进程拥有该服务信息

获取服务
* Client向Binder驱动发起获取服务的请求,传递要获取的服务名称(service name)
* Binder驱动将该请求转发给ServiceManager进程
* ServiceManager查找到Client需要的Server对应的服务信息
* 通过Binder驱动将上述服务信息(服务代理对象)返回给Client进程
* 此时client进程与server进程已经建立了连接

使用服务
* Client进程将参数数据发到Server进程
    1. client 进程将需要的传送的数据放到client进程的共享内存;(当前线程被挂起)
    2. Binder驱动从client的共享内存中读取数据,并根据ServiceManager进程里面的Server信息找到对应的Server进程
    3. Binder驱动将数据copy到Server进程的共享内存里,并通知Server进程解包
* Server进程根据Client进程要求,调用目标方法
    1. 接到Binder驱动通知后,Server进程从线程池中取出线程,进行数据解包和调用目标方法
    2. 将最终方法结果写到自己的共享内存
* Server进程将目标方法的结果,返回给Client进程
    1. Binder驱动程序将Server进程的共享内存里面的数据(方法执行结果) copy 到client进程的共享内存
    2. 通知client进程获得返回结果(此时client进程之前被挂起的线程被重新唤醒)
从客户端来看进程被挂起,binder通信是同步调用;
但是在服务端却是收到消息后从线程池中取出线程进行处理并将处理结果返回,在服务端是异步处理的;

Client进程、Server进程 & Service Manager 进程之间的交互 都必须通过Binder驱动(使用 open 和 ioctl文件操作函数),而非直接交互
Client进程、Server进程 & Service Manager 进程属于进程空间的用户空间,不可进行进程间交互
Binder驱动 属于 进程空间的 内核空间,可进行进程间 & 进程内交互

Binder驱动 & Service Manager进程 属于 Android基础架构(即系统已经实现好了);
而Client 进程 和 Server 进程 属于Android应用层(需要开发者自己实现)


更多细节请参阅读参考资料。

参考资料:
Android技术内幕-系统卷
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值