Android Binder机制彻底梳理一

Binder架构图:

先来瞅一下它的整体架构图:

其中粉红部分是上层的Binder,而蓝色的则是下层的Binder,很显然上层的是依赖于下层的。 

什么是Binder【有个大概了解】?

这里从几个层面来说一下:

从IPC角度来说:

定义:Binder是Android中的一种跨进程通信方式,该通信方式在linux中没有,是Android独有的。

作用:在Android中实现跨进程通信。 

从Android Driver层来说:

其实也就是指这一层:

定义:Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder。

备注:驱动层位于Linux内核中,它提供了最底层的数据传递,对象标识,线程管理,调用过程控制等功能。驱动层是整个Binder机制的核心。

从Android Native层来说:

这一层:

定义:Binder是创建Service Manager以及BpBinder/BBinder模型,搭建与binder驱动的桥梁。

从Android Framework层来说:

定义:Binder是各种Manager(ActivityManager、WindowManager等)和相应xxxManagerService的桥梁。 

从Android APP层来说:

定义:Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的 Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

Binder核心源码分析【通过底层C来纵览】:

我们平常不管在是学习还在是面试时聊天Android的跨进程技术都会说到Binder,那它到底长啥样呢?我们知道它底层是用C来实现的,所以接下来从内核层的角度去认识一下它,当然里面的细节可能看不懂,但有个大概印象既可,这样会加深对Android的Binder跨进程机制有一个更加深刻的理解,而非只停留在理论层面。那既然要分析内核层的代码,得先找到相关代码嘛,这里采用之前【https://www.cnblogs.com/webor2006/p/10708754.html】分析Android的热修复原理时说到的一个方式来查看:http://androidxref.com/,如下:

Binder驱动:

首先来瞅一下Binder驱动,也就是这块东东:

 

其中图中的这个东东到底是对应哪段代码:

还有这个东东确实是存在么?

或者说上图中画的到底准不准确?所以一切答案都得从源码中来寻找答案,下面就开始找寻答案,对于Binder来说,它其实在底层是对应这个代码:binder.c,咱们在线找一下它:

所以不可能全部都分析到,主要分析一下主流程,下面开始。

binder_init():驱动设备初始化。

先来找到这个函数:

 

具体里面注册了啥这里就不深究了,重在了解注流程。

binder_open():打开Binder驱动设备。

初始化了之后,接下来则会打开Binder驱动设备,简单分析一下对应代码:

binder_mmap():申请内存。

它的作用先贴一下,有个了解,反正细节也不太懂:“首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请1个page大小的物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的Buffer和内核空间的Buffer同步操作的功能。”

看一下该函数:

binder_ioctl():数据操作。

之前提出的问题的答案就出现了,回忆下:

它确实在底层对应的代码,图没有画错,看下代码:

然后它里面有很多操作,大概瞅一下:

好,大致过一下,晕晕的,反正大概知道在底层确实是有一个Binder驱动这个东东。

ServiceManager:

先看咱们的架构图:

对于服务的注册和服务的获取我们都需要通过ServiceManager,那下面看下它的一些主流程,当然它也有对应的c代码啦,它的代码是在Android系统的Native层了,而非Android的内核层,所以这里以9.0系统为例搜一下它:

下面则来分析一下主流程:

启动ServiceManager:

先来看一下这个启动流程的时序图,有个全局观:

下面先来看一下它的启动入口在哪?

好,接下来依据流程图来分析一下:

  • binder_open()打开binder驱动。

    对应代码:


    而此方法其实调用的是binder.c的,所以其做的事就是时序图中画的2、3、4,如下:

    这个在分析binder驱动源码时都有看到过相关的函数,下面再简单介绍一下:

  • binder_become_context_manager() 注册成为binder服务的大管家。
    先看一下时序图对应的流程:

    对应的代码处:

     而它的实现是这样的:

     也就是是通过ioctl() 让其成为上下文的管理者,而该函数最终会经过系统调用调用Binder驱动层的binder_ioctl()方法,整个系统中只有一个这样的管理者。

  • binder_loop() 进入无限循环,处理client端发来的请求。
    对应时序流程的这块:

    找下代码:

     
    最后则会调用这个函数中的do_add_service()和do_find_service(),如下:

     

获取ServiceManager:

也来看一下获取的时序图:

下面再来看个描述:

获取Service Manager是通过defaultServiceManager()函数来完成,当进程注册服务(addService)或获取服务(getService)的过程之前,都需要先调用defaultServiceManager()方法来获取gDefaultServiceManager对象。对于gDefaultServiceManager对象,如果存在则直接返回;如果不存在则创建该对象,创建过程包括调用open()打开binder驱动设备,利用mmap()映射内核的地址空间。

根据时序图画的,先找到IServiceManager,里面有个defaultServiceManager()方法,下面简单分析一下:

用于获取BpServiceManager对象。

它是用于获取ProcessState对象(也是单例模式),每个进程有且只有一个ProcessState对象,存在则直接返回,不存在则创建。

它用于获取BpBinder对象,对于handle=0的BpBinder对象,存在则直接返回,不存在才创建。其实也就是对应这块的东东:

do_add_service()注册服务:

接下来再来看一下注册服务的大体流程:

 

然后咱们也来看一张时序图,以注册MediaPlayerService为例,如下:

按着时序图来大致分析一下:

所以瞅一下这个添加服务的方法:

 

然后:

也就是:

继续照着流程分析:

而它里面的实现主要是:

 

这里就不再继续往下分析了,反正最终最终会调用这来:

里面大致分析一下:

svc_can_register() 检查权限,检查selinux权限是否满足。

find_svc() 服务检索,根据服务名来查询匹配的服务。

 

svcinfo_death() 释放服务,当查询到已存在同名的服务,则先清理该服务信息,再将当前的服务加入到服务列表svclist。

do_find_service()查询服务:

主要是看一下这块:

find_svc() 从svclist服务列表中,根据服务名遍历查找是否已经注册。当服务已存在svclist,则返回相应的服务名,否则返回NULL。

framework层分析:

这里先来看一个Binder相关架构的核心类图:

接下来咱们得往Frameworkd层进行分析了,如下:

初始化:

在Android系统开机过程中,Zygote启动时会有一个虚拟机注册过程,该过程调用AndroidRuntime::startReg方法来完成jni方法的注册,所以咱们从这块进行分析:

找到对应的代码:

然后看一下startReg()方法:

而其中这个数组中就有注册Binder的函数,如下:

 而该函数的定义是在这里:

对应代码为:

 

下面简单分析一下这三个注册函数:

int_register_android_os_Binder():注册Binder,建立了Binder类在Native层与framework层之间的相互调用的桥梁。

int_register_android_os_BinderInternal():注册BindrInternal,建立了BinderInternal类在Native层与framework层之间的相互调用的桥梁。

int_register_android_os_BinderProxy():注册BinderProxy,建立了BinderProxy类在Native层与framework层之间的相互调用的桥梁。

注册服务:

此时就会涉及到Java代码的分析了,主要是这块:

其添加的入口是在:

而它最终调用的是它:

 

也就是:

获取服务:

它的入口也是在ServiceManager当中,如下:

 

其中最终会调用ServiceManagerProxy代理类的getService(),如下:

手写Binder通信--AIDL:

经过了漫长枯燥的源码分析已经对Binder机制有了进一步了解之后,下面咱们来撸撸码来写一个AIDL,然后写完之后再以这个例子再来对它进行一个原理剖析,这样对于Binder的机制应该了解得就非常之透了,由于这块的写法比较简单就不多说,重点是通过这个例子好分析整个它的背后机制。好,下面开始撸码,既然是C/S结构,则先新建两个app,一个代表c端,一个代表s端:

编写服务端:

在服务端中新建一个AIDL文件,如下:

这里以一个添加人员的非基础类型为例,基础类型的比较简单这里就不以它为例了,先新建一个Person类:

它里面定义为:

public class Person implements Parcelable {

    private String name;
    private int grade;

    public Person(String name, int grade) {
        this.grade = grade;
        this.name = name;
    }

    protected Person(Parcel in) {
        this.grade = in.readInt();
        this.name = in.readString();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int i) {
        dest.writeInt(this.grade);
        dest.writeString(name);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", grade=" + grade +
                '}';
    }
}

然后再定义一个这个类的AIDL文件:

然后再来定义咱们的IMyAidl:

好,接下来则定义一个服务:

这样系统就会扫码已经定义在清单中的所有服务然后将其注册到我们的ServiceManager当中,也就是:

最后咱们在应用启动时来将此服务启动一下,如下:

 

然后咱们就运行将它启动一下:

接下来编写咱们的客户端代码了。

编写客户端:

首先将服务端的AIDL定义的文件拷进来,注意要连包名目录是一模一样的,如下:

然后它里面用到了一个实体类Person,也需要原包名进行复制,如下:

然后咱们再来启动一下服务端的那个服务,其代码如下:

public class MainActivity extends AppCompatActivity {

    private IMyAidl myAidl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }

    private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.binderserver", "com.android.binderserver.MyBinderService"));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            myAidl = IMyAidl.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}

然后咱们增加几个点击按钮,来看一下整个的效果:

处理下点击事件:

运行看一下效果:

以上就是一个AIDL的完整过程,也就是Android跨进程通讯的典型案例,由于篇幅原因关于它的整个流程梳理放到下次。

posted on 2019-10-26 08:05  cexo  阅读(1542)  评论(0编辑  收藏  举报

导航