深入剖析Android中Binder实现原理(一

709 阅读6分钟
原文链接: mp.weixin.qq.com

Android中进程间通信采用Binder机制实现,是Android系统中几大重要机制之一,本文将从整体架构和底层源码分析Binder机制的实现原理。由于Binder机制涉及到的东西比较多,因此这里会分成几篇文章进行介绍。

一、整体架构

Binder架构分为三层:Java层,C++层和驱动层。对于应用开发,我们使用的大部分都是Java层接口,而Java层最终会通过JNI进入C++层,因此C++层才是真正实现Binder机制的地方,而C++层实现进程间通信需要驱动层的支持,驱动层运行在内核中,所有进程都可以调用其公开的接口。

二、驱动层

Binder驱动其实就是一段运行在Linux内核中的C代码,可以在https://github.com/torvalds/linux/blob/master/drivers/android/binder.c查看,驱动层做的工作主要有两个,第一是处理用户空间进程的进程通信请求,第二个是记录维护各个进程的进程间通信信息。

三、C++层

C++层在Binder机制实现中是最复杂的,主要会涉及BpBinder,BBinder,BpInterface,BnInterface,IPCThreadState等相关类。

在C++层,如果一个进程要成为服务进程,其它进程可以调用该进程相关接口,按照上面的类图,该进程需要定义一个IXXX的接口,声明该进程可以进行远程调用的接口,还需要定义三个类:BpXXX,BnXXX,XXXService,其中BpXXX将在调用进程中被使用,最终都是通过remote() ---> XXX进行调用的,而BnXXX和XXXService是在服务进程中被使用,上层最终将会回调BnXXX相关的接口方法,而XXXService继承自BnXXX,真正实现相关的接口方法。

C++层还管理了一个Binder线程池,相关的类为ProcessState和IPCThreadState。进程启动时会创建一个ProcessState对象,该对象会进行相关初始化Binder操作,包括:打开驱动,进行内存映射,启动Binder线程池等。

可以看到,一个进程有且仅有一个ProcessState对象。而最终C++层与Binder驱动通信的类为IPCThreadState,对于Binder线程池中的每一个线程,都有一个IPCThreadState对象,该对象会调用Binder驱动对外提供的接口,实现与Binder驱动进行通信。

四、Java层

Java层比较简单,主要就是提供给开发者在Java层实现进程间通信的相关类和接口。通过AIDL语言定义IXXX接口,然后编译器会自动生成相关的类,Proxy类将在调用进程被使用,该类只是一个代理类,最终会通过C++层进行进程间通信,而Stub类在本地进程被使用,通过不同的命令调用对应的接口,接口最终实现是在XXXService类中。

五、一次完整的进程间通信流程分析

该例子只分析在Java层实现进程间通信情况下的通信流程。对于C++层实现的进程间通信原理类似可以自行分析。

首先先把Demo写好跑起来:

1.新建服务端工程,定义一个IPayService.aidl,里面定义了一个支付接口:

2.clean项目,编译器自动生成了IPayService接口,包括内部类Stub和Proxy

3.定义BnPayService类,继承自Stub,实现pay接口,这里只是打印一下参数,然后定义PayService,在onBind方法中返回一个BnPayService对象

4.服务端已经实现好了,直接运行到手机上

5.新建客户端工程,把服务端IPayService.aidl生成的相关包复制进来,通过bindService绑定到服务端的Service上,在onServiceConnected回调中拿到一个IPayService对象

6.点击按钮,调用pay接口,可以看到服务端进程打印出日志,说明进程间调用成功

接下来从客户端调用pay方法开始,简要分析整个流程:

  1. 在onServiceConnected回调中,我们首先会通过asInterface得到一个IPayService对象,而asInterface判断当前进程是不是服务端进程,如果是,就返回真正的本地对象,如果不是就返回一个代理对象,也就是编译器自动生成的Proxy类对象。主要asInterface要求传入一个IBinder对象作为参数,该IBinder对象时框架层传过来的,它其实是一个BinderProxy对象。

  2. 接着调用pay,因为我们是在另外一个进程,因此调用Proxy的pay方法,这里会将数据进行打包,然后调用mRemote的transact方法,而这个mRemote,就是上面说的BinderProxy对象。

  3. BinderProxy是Binder的内部类,再去看BinderProxy的transact方法,发现调用本地方法了,调用了transactNative方法。

  4. 来到android_util_Binder.cpp类的android_os_BinderProxy_transact函数,这里会先把Java层的Parcel数据转成C++层的Parcel数据,然后获取得到一个IBinder*对象,这个对象就是C++层的BpInterface对象,然后调用其transact函数,其内部会转发到remote()的transact函数,而remote返回的是一个BpBinder对象。

  5. BpBinder的transact函数,可以看到直接使用IPCThreadState跟驱动通信了,在IPCThreadState的函数transact中,先准备好数据,最终会得到一个binder_transaction_data结构体数据,然后调用waitForResponse开始向驱动发请求

  6. 在waitForResponse函数中,开个死循环不断地跟驱动交互,直到本次通信结束为止。对于交互细节,IPCThreadState会调用talkWithDriver函数,这个函数从名字就可以看出是发送请求给驱动,它最终会调用ioctl函数,对于的就是驱动的binder_ioctl函数,然后会有个cmd码,IPCThreadState拿到这个cmd码后,如果Binder通信正常,这个cmd码就是BR_REPLY。

  7. 再来看看驱动接收到IPCThreadState的请求后是如何处理的。看binder_ioctl函数,首先会请求获取目标进程的一个处理线程,目标进程会根据情况进行创建,如果已经超过最大线程数就返回空。binder获取到线程后,调用binder_ioctl_write_read函数。

  8. binder_ioctl_write_read函数中,最终会调用binder_transaction函数,这里会创建事务,加到目标进程的等待队列中,然后唤醒目标进程。

  9. 目标进程的ProcessState在创建时会有个线程一直在睡眠,等待请求到来,这时就会被唤醒了,然后执行IPCThreadState的executeCommand函数。

  10. 在executeCommand函数中,来到BR_TRANSACTIOn分支,有行重要代码: error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags);

  11. 来到BBinder的transact函数,这里最终会调用到PayService类中onBind方法返回的那个对象的pay方法。

六、其它

Binder机制相当复杂,关于内存分配及映射,ProcessState和IPCThreadState实现原理,驱动层进程数据结构解析等将在下篇文章介绍。