前语

IPC 系列文章:
主张按顺序阅读。

Android IPC 之Service 还能够这么理解
Android IPC 之Binder根底
Android IPC 之Binder运用
Android IPC 之AIDL运用(上)
Android IPC 之AIDL运用(下)
Android IPC 之Messenger 原理及运用
Android IPC 之服务端回调
Android IPC 之获取服务(IBinder)
Android Binder 原理换个姿态就彻悟了(图文版)

Binder机制可谓是Android 常识体系里的重中之重,作为偏底层的根底组件,平时我们很少关注它,而它却是无处不在,也是Android 面试易调查的点之一。网上许多文章,要么常识点比较陈腐,要么源码贴一堆,要么没有成体系地剖析,导致读者一知半解,貌同实异。
本篇将从流程上将Binder通讯过一遍,尽量多用图展现。
经过本篇文章,你将了解到:

  1. Binder的效果
  2. 进程与Binder驱动怎么通讯
  3. ServiceManager进程的效果
  4. 进程增加服务到ServiceManager的流程
  5. 进程从ServiceManager获取服务的流程
  6. Binder服务端数据接纳
  7. Binder 通讯全流程图

1. Binder的效果

先看Linux下进程地址映射联系:

Android Binder 原理换个姿势就顿悟了(图文版)

我们知道,目标调用自身便是地址空间的拜访。
如上,进程之间各自拜访各自的内存地址,它们之间无法直接拜访对方的地址,也便是说微信不能直接调用支付宝供给的接口。而内核具有拜访其它进程地址空间的权限,因此微信能够将音讯发送给内核,让内核帮忙转发给支付宝,这种办法叫做:存储/转发办法。
由此衍生的几种IPC(进程间通讯)如:管道、音讯队列、socket等,而Android 上采用了新的机制: Binder,相比传统的办法,Binder只需求一次数据拷贝,而且Binder更安全。

Binder机制是Android 里用来做IPC的主要办法。

2. 进程与Binder驱动怎么通讯

已然得要内核进行音讯中转,那么Binder驱动得运转在内核空间,而事实上也确实如此,Binder驱动加载后在内核空间运转,进程只需求和Binder驱动取得联系,经过Binder驱动联系另一个进程,那么一次音讯的传送进程就能够完成了。

Android Binder 原理换个姿势就顿悟了(图文版)

内核供给供给一系列的体系调用接口给用户进程运用,当用户进程想要拜访内核时,只需求调用对应的接口,此刻代码就会从用户空间切换到内核空间履行。
常见的体系调用函数如:open/read/write/ioctl/close/mmap/fork 等。
与Binder驱动通讯分两步:
  1. 翻开Binder驱动:open(“/dev/binder”, O_RDWR | O_CLOEXEC)
  2. 经过ioctl 与Binder驱动进行数据通讯:ioctl(mDriverFD, BINDER_WRITE_READ, &bwr)
    bwr 为读写数据结构

3. ServiceManager进程的效果

Binder Client、Binder Server、ServiceManager联系

为方便起见,ServiceManager简称SM。
Binder 规划为C/S架构,C为Client(客户端),S为Server(服务端),Server端供给接口(服务)给Client端运用,而这个服务是以Binder引证的形式供给的。
由之前的常识可知,C和S是不同的进程,那么C怎么拿到S的Binder引证呢?
你或许会说,当然是SM了,S先将Binder引证存放在SM里,当C需求的时分向SM查询即可。
这么看好像讲得通了,那问题又来了,SM也是一个单独的进程,那S、C怎么与SM进行通讯呢?这就陷入了先有鸡仍是先有蛋的死循环了。
实际上C、S、SM之间都是依托Binder通讯,仅仅SM作为特殊的Binder(handle=0)提前放入了Binder驱动里,当C、S想要获取SM的Binder引证,只需求获取handle=0的Binder即可。
这么说没有太直观的形象,我们一步步剖析。

ServiceManager注册进Binder

Android Binder 原理换个姿势就顿悟了(图文版)

SM 注册进Binder驱动后就会等待来自Binder驱动的音讯,这儿列出了两个最常见的处理音讯的Case:

  1. 其它进程增加服务到SM里
  2. 其它进程向SM查询服务

SM里保护着一个链表,链表的元素是结构体:

Android Binder 原理换个姿势就顿悟了(图文版)

主要记录的是name和handle字段。
当SM收到增加服务的指令后,从Binder驱动里取出handle和name,并结构结构体刺进到链表。
当SM收到查询服务的指令后,从Binder驱动里取出name,并找到链表里相同的name,找到后取出handle,终究写入到Binder驱动。

4. 进程增加服务到ServiceManager的流程

其它进程找到SM

现在SM现已翘首以盼其它进程的请求了,接着来看看怎么增加一个服务到SM里。
Java层增加服务为例,我们选择振荡服务作为切入点剖析。

Android Binder 原理换个姿势就顿悟了(图文版)

在system_server 进程里结构振荡服务(VibratorService继承自Binder),并增加到SM里。

Android Binder 原理换个姿势就顿悟了(图文版)

能够看出,分两步:
  1. 先找到ServiceManager
  2. 往ServiceManager里增加服务

getIServiceManager()继续往下:

Android Binder 原理换个姿势就顿悟了(图文版)

BinderInternal.getContextObject() 是native办法,后续流程较多,我们用图表明。

Android Binder 原理换个姿势就顿悟了(图文版)

寻找ServiceManager的进程涉及到Java层和Native层,主要的要点在Native层查找 ServiceManager对应的BpBinder目标,没有找到的话则创立新的并存入缓存里以备下次直接获取。

  1. ProcessState里保护了一个单例,每个进程只需一个ProcessState目标,创立ProcessState时分就会去翻开Binder驱动,同时会设置Binder线程池里线程个数等其它参数
  2. Native层结构BpBinder(handle=0表明该BpBinder是ServiceManager在客户端的引证),再结构BinderProxyNativeData持有BpBinder。
  3. 结构BinderProxy目标并持有BinderProxyNativeData,也便是间接持有BpBinder
  4. 终究结构了ServiceManagerProxy目标,它完成了IServiceManager接口,它的成员变量mRemote指向了BinderProxy

能够看出,获取ServiceManager的进程并不是真实去获取ServiceManager的Binder目标,而是获取它在当前进程的署理:BpBinder

增加服务到ServiceManager

已然找到了SM的Binder署理,接下来看看怎么运用它给SM增加服务。

    #ServiceManagerNative.ServiceManagerProxy
    public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
            throws RemoteException {
        //结构Parcel
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        //写入Binder
        data.writeStrongBinder(service);
        data.writeInt(allowIsolated ? 1 : 0);
        data.writeInt(dumpPriority);
        //经过BinderProxy发送
        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
        reply.recycle();
        data.recycle();
    }

Android Binder 原理换个姿势就顿悟了(图文版)

其中IPCThreadState与线程相关,不同的线程会保护一个单例。
由此可见,终究仍是经过BpBinder发送音讯,然后发送到Binder驱动。
此刻驱动收到的信息包含不限于:
  1. 服务的姓名
  2. ServiceManager的handle
  3. BBinder目标指针

驱动建立服务handle和BBinder目标指针的映射联系,并将服务的姓名和服务的handle传递给ServiceManager(经过ServiceManager handle查找)。
ServiceManager拿到音讯后建立映射联系,等待其它进程的请求。
至此,进程增加服务到ServiceManager进程现已剖析完毕,用图表明如下:

Android Binder 原理换个姿势就顿悟了(图文版)

BBinder效果

Java层传递的是Binder目标,怎么与Native的BBinder相关起来呢?
要点在:

Parcel.writeStrongBinder(Binder)

Android Binder 原理换个姿势就顿悟了(图文版)

也即是说Server端的Java Binder目标在Native层的代表是BBinder。
Binder驱动记录了BBinder的地址,当有音讯过来时经过找到BBinder目标然后找到Java层的Binder目标,终究调用Binder.onTransact()。

5. 进程从ServiceManager获取服务的流程

其它进程找到SM

振荡服务增加完成后,某些进程想要获取振荡服务进行振荡,比如微信收到音讯后需求振荡用以提示用户。
接着来看看怎么获取振荡服务。

    private void vibrate() {
        //获取振荡服务
        Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
        //开始振荡
        vibrator.vibrate(1000);
    }

与增加服务类似,想要获取服务先要找到SM,找SM的进程上边剖析过了,此处不再细说。

从ServiceManager获取服务

    #ServiceManagerNative.ServiceManagerProxy
    public IBinder getService(String name) throws RemoteException {
        //结构Parcel
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        //写入姓名
        data.writeString(name);
        //经过BinderProxy发送
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }

Android Binder 原理换个姿势就顿悟了(图文版)

由此可见,终究仍是经过BpBinder发送音讯,然后发送到Binder驱动。
此刻驱动收到的信息包含不限于:
  1. 服务的姓名
  2. ServiceManager的handle

Binder驱动收到音讯后,找到SM,并将服务的姓名传给SM,SM从自己保护的链表里找到服务名相同的节点,终究取出该服务的handle,发送给Binder驱动。
用图表明如下:

Android Binder 原理换个姿势就顿悟了(图文版)

对比增加服务流程和获取服务流程,两者前半部分都很类似,都是先拿到SM的BpBinder引证,然后写入驱动,终究由SM进程处理。仅仅关于获取服务流程来说,还需求将查询的成果(handle)写入驱动返回给调用方(对应图上赤色部分)。

到这,我们或许会有疑问了:“handle是整形值,而微信获取的振荡服务是一个Binder目标,这两者是怎么结合起来的呢?”

handle转换为Binder目标

handle表明的即是Binder服务端在客户端的索引句柄,只需客户端拿到了handle,它就能经过Binder驱动调用到服务端。

        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        IBinder binder = reply.readStrongBinder();

再回过头看看获取服务的代码,当微信进程将查询指令发给Binder驱动后就等待驱动回复的成果,SM查询到成果后将handle写入驱动,然后微信进程从驱动将成果读出并将成果存入reply字段。
终究经过reply拿到Binder引证,也便是说要点在reply.readStrongBinder()办法。
直接看图:

Android Binder 原理换个姿势就顿悟了(图文版)

如上,经过驱动返回的handle结构BpBinder,终究封装为Java层的BinderProxy。

至此,获取服务流程就结束了,用图展现简化的流程

Android Binder 原理换个姿势就顿悟了(图文版)

6. Binder服务端数据接纳

微信进程拿到振荡服务(在system_server进程里)的Binder(BinderProxy)后,就能够调用振荡办法了,然后指令发送给驱动,驱动经过振荡服务的handle找到对应的服务BBinder指针,然后调用服务的接纳办法。
微信进程发送指令给Binder驱动前面现已剖析过,要点来看看system_server进程是怎么接纳并处理指令的。

Android Binder 原理换个姿势就顿悟了(图文版)

system_server进程发动的时分就会敞开Binder线程池,并等待驱动数据到来。
当system_server进程增加振荡服务到SM时,会将Java层的Binder转为Native层的BBinder,并将BBinder目标指针写入Binder驱动。
当微信进程调用system_server接口时:
  1. 微信进程调用BpBinder.transact()将handle和数据写入Binder驱动
  2. Binder驱动依据handle找到system_server进程
  3. system_server进程从驱动拿到数据,并取出BBinder指针,终究调用到system_server进程Java层的Binder.onTransact()

如此一来,微信成功调用了振荡服务,也便是说一次Client到Server端的通讯就完成了。

7. Binder 通讯全流程图

纵观Binder机制规划,最核心的点是handle。

  1. 经过handle结构Client端的BpBinder(Native层),与此对应的是Java层的BinderProxy
  2. 经过handle,驱动找到Server端进程,然后调用BBinder(Native层),与此对应的是Java层的Binder
  3. 经过handle的一系列中转,Client.transact()成功调用了Server.onTransact(),一次Binder通讯就进程就完成了

终究,用一张图总结Binder机制的全进程:

Android Binder 原理换个姿势就顿悟了(图文版)

以上便是整个Binder机制的梳理进程,此间省略了Binder驱动里的映射逻辑,能够将Binder驱动作为一个黑盒,而更重要的是Binder客户端和服务端是怎么进行映射的。
Binder流程比较绕,尤其是IPCThreadStsate作为客户端的发送和服务端的数据接纳的实体,需求区别不同的场景。
当然,jni根底常识必不可少。

本文根据Android 10
限于篇幅并没有一步步列出源码,对源码细节有疑问之处欢迎留言讨论。

您若喜欢,请点赞、关注、收藏,您的鼓舞是我前进的动力

继续更新中,和我一起步步为营体系、深化学习Android/Kotlin

1、Android各种Context的宿世今生
2、Android DecorView 必知必会
3、Window/WindowManager 不可不知之事
4、View Measure/Layout/Draw 真明白了
5、Android事情分发全套服务
6、Android invalidate/postInvalidate/requestLayout 完全厘清
7、Android Window 怎么确定大小/onMeasure()屡次履行原因
8、Android事情驱动Handler-Message-Looper解析
9、Android 键盘一招搞定
10、Android 各种坐标完全明晰
11、Android Activity/Window/View 的background
12、Android Activity创立到View的显现过
13、Android IPC 系列
14、Android 存储系列
15、Java 并发系列不再疑问
16、Java 线程池系列
17、Android Jetpack 前置根底系列
18、Android Jetpack 易学易懂系列
19、Kotlin 轻松入门系列
20、Kotlin 协程系列全面解读