android binder 机制概述

       首先从概念上来说,什么是Binder,简单来说Binder是一种跨进程的通讯方式,从Android Framewok 来说,binder是serviceManager连接各种manger 和相应ManagerService 的桥梁,而binder 可以充当进程的桥梁,它是android ipc 机制中的一种。

       直观的使用就是aidl,AIDL是Android中IPC(Inter-Process Communication)方式中的一种,AIDL是Android Interface definition language的缩写(对于小白来说,AIDL的作用是让你可以在自己的APP里绑定一个其他APP的service,这样你的APP可以和其他APP交互。)AIDL只是Android中众多进程间通讯方式中的一种方式,从另一种角度来讲,aidl 其实是对binder 跨进程通讯使用的一种封装,我们只需要定义一个后缀名为aidl 的文件,并且定义相关使用的接口,然后通过工具构建,android 会帮助我们在build/generated/aidl_source_output_dir/debug/compileDebugAidl/out/com/example/aidlapplication/IMyAidlInterface.java,创建一个对应的java 文件。aidlapplication 是我的应用名,IMyAidlInterface 是我定义的aidl 文件名,这里会对应生成一个java接口类,这个类才是我们进程通讯的关键所在,它通过binder 机制来实现进程间的通信的。

     那么问题来了,android为什么要binder 来做进程通信,直接通信不行么?

大家都知道Android是基于Linux内核的操作系统,那么一般来说我们的应用当然就是一个linux进程了,linux进程又是相互独立的,我们叫做进程隔离,这里主要是出于安全的考虑,每个android 进程app只能运行在自己进程的虚拟地址空间,虚拟地址空间又分为用户空间和内核空间,这里有两个隔离。

    1.不同进程之间是相互隔离的。

    2.进程内有用户空间和内核空间的隔离。

所以说进程间是不能直接的通信的,所以就有了binder ,这里有一点需要注意,那就是每个进程分为用户空间和内核空间,第一部分为“用户空间”,用来映射其整个进程空间(0x0000 0000-0xBFFF FFFF)即3G字节的虚拟地址;第二部分为“系统空间”,用来映射(0xC000 0000-0xFFFF FFFF)1G字节的虚拟地址。那是不是n多个进程就会有n多个内核空间和用户空间呢,其实不是的,用户空间是有n多个,但是内核空间只有一个或者说是共享的,他是由内核映射的,内核空间是所有进程共享的,而用户空间是不能共享的,所以内核空间才会成为多个进程数据传输的一个桥梁。那么我们所说的binder进程间通信主要就是共享内核空间,用户进程通过用户地址空间来访问这块内核缓冲区的内容,而binder 驱动使用内核空间地址来访问这块缓冲区的内容,这是binder 进程间通讯的关键所在。

那他们到底是如何通信的呢,我们假设进程A为客户端,B 为服务端,那么B 要先在serviceManager(以后简称smg) 中注册,通过注册,smg获取到b中的binder 对象的引用,并且使用一个单链表将它保存起来。现在A 要和B进行通信,首先通过绑定B中注册的services ,获得一个iBinder 接口对象,这个过程实际就是A通过binder 驱动 获取到一个binder代理BinderProxy对像,A通过代理对象BinderProxy  调用接口方法,BinderProxy对象再通过binder 驱动,从serviceManager 中获取binder引用,然后调用binder 所定义的接口方法,从而达到进程间通信的,在整个调用的过程中是同步进行的。

 这里主要有三个方面,服务端的注册和serviceManager 的注册,客户端的绑定

1.服务端的注册

   server注册binder 实体信息到smg 的时候请求数据需要该binder 实体信息的描述信息,这个在之后进行查询的时候smg根据描述信息来获取对应的binder 引用编号,每个通过binder 通信的进程都要打开binder 驱动一次(至多一次)server也不例外,打开binder 驱动后,内核会调用驱动程序的binder_open方法,该方法内部将会创建binder_proc 结构体,并存储了进程信息及uid信息,这也是binder通信安全的一个原因,server 的注册是为了后边,客户端与其通信时能够顺利建立连接(拿到客户端binder 代理对象),从而实现跨进程通信。

 

2.serviceManager 的注册

       serviceManager 的注册过程大致分五步

1.打开binder 驱动

2.将自己注册为binder 驱动 的大管家(其他进程根据引用编号0可以找到smg 对应的binder 实体)

3.进入循环不断从binder 驱动 中读取消息(无消息时被阻塞)

4.读取到消息后处理消息

5.不断循环,永不退出

serviceManager 通过binder_open 打开驱动,将自己注册为驱动的大管家,binder驱动创建servicemanager 的binder 实体对象引用编号为0,并且所有的客户端都知道,smg进入无限循环状态,不断从binder 驱动中读取消息,读取到消息后,根据消息的描述信息返回对应的binder 对象引用。

在这个过程中出现了 Ibinder , IInterface ,Binder, BinderProxy ,stub,servicesManager,  binder 驱动 , 一些关类或者工具。

1.IBinder 是一个接口,它代表一种跨进程传输的能力,实现这个接口,就能将这个对象进行跨进程的传递,它是远程对象的基本接口,是为高性能而设计的的轻量级远程调用机制的核心部分(为啥说是高性能这个后边会讲到),但是它不仅用于远程调用,也用于进程内部调用,这个接口还定义了与远程对象交互的协议,但是使用的时候不要直接实现这个接口,而因该从Binder派生,关键方法transact()。

2.IInterface 接口,是clienter 端和server 端的调用契约,实现这个接口就代表远程server对象具有什么能力,因为IInterface 接口的asBinder方法的实现可以将Binder 本地对象或者代理对象进行返回。

3.Binder 对象,binder 机制中进程间通讯的对象,对于service 端为Binder本地对象,对于client端为Binder代理对象BinderProxy,是一个可以跨进程引用的对象,它的实体位于进程中,而他的引用却遍布系统的的各个进程中,这个引用和java里的引用一样,即可以是强引用也可以是弱引用,可以从一个进程传给其他进程,让大家访问同一个server,就像一个对象引用负值给另一个对象一样,是serviceManager连接各个manager 和相应ManagerService的桥梁,而binder 可以充当两个进程的桥梁,关键方法onTransact()。

4.BinderPraxy 类:是binder 类的内部类,它代表进程的Binder对像的本地代理,因继承自IBinde,因而具有跨进程传输的能力,在跨进程时,Binder 驱动会自动完成两个对象的转换。

5.stub:AIDL的时候编工具给我们生成的一个名为stub 的静态内部类,这个类继承自Binder ,说明他是一个本地Binder 本地对象,它实现了IInterface 接口 ,表明它具有server 承诺给clente的能力。stub是一个抽象类具体IInterface 要开发者自己实现,这个名字是工具自动给订的,我们可以自己修改,但是要保证两端一致。

6.ServicesManager 运行在一个单独的进程中,他集中管理系统内的所有的服务,通过权限控制进程是否有权注册服务,通过字符串名称来查找对应的service,在binder 进程通信机制中扮演着上下文管理的角色,他的核心工作就是注册和查询服务,注册时他会存储当前service 的binder 对象引用,到一个单链表中。

7.Binder 驱动:它是属于内核空间的,属于整个通信的核心,虽然叫驱动,但是实际上和硬件没太大关系,只是实现方式和驱动差不多,Binder 机制中进程间通讯的介质,binder驱动会对具有跨进程传输能力的对象做特殊处理,自动完成代理对象和本地对象的转换,serviceManager启动后将打开binder 驱动,并将自己注册为binder 驱动的管家,其他进程根据引用编号0可以找到smg(servicemanager)对应的binder 实体,从而和serviceManage建立通信,servicemanager 返回服务端的binder引用。

这是binder 机制通信中的几个重要角色,从整体上Binder 框架定义了四个角色:server ,client,servicesManager,以及Binder 驱动。

server 服务端,运行在一个单独进程的用户空间

client  客户端,同样运行在一个单独的进程用户空间中

servicesManager 一个服务的管理进程,同样运行在一个单独进程的用户空间中

binder 驱动程序 运行在内核空间中.

通过上面的理解,我们可以这样理解binder 通信的整个过程

binder分配的默认的内存大小为1M- 8k,普通应用最大传输为

(1*1024*1024) - (4096 *2)

约1016kb不到1M ,默认的最大可并发访问的线程数为16,当然我们一般使用中也不会超过这个限制,超出会报错,最常见的就是intent 传值。

binder 其实非常复杂,他在应用层,frameWork 层 native jni 及内核层都有实现,他贯穿了整个androd 系统架构,是整个android进程间通信的桥梁,大到我们所说的进程间数据通信,小到两个activity页面的启动,都有用到binder 通信。

基本概念及原理清楚了,那么我上面说binder 是一种高效的进程间通信,到底是如何高效呢?

在这之前我们先大概了解下进程间通行的几种方式,pipe管道,消息队列,socket,共享内存,binder

   1.binder android 特有的跨进程通信,需要一次数据拷贝

  2. pipe ,消息队列,socket 都需要将数据拷贝两次

  3.共享内存,标准的linux ipc 机制,需要0次数据拷贝

binder 内存管理机制

 主要原理是接收端的虚拟进程地址空间和虚拟内核地址空间,都映射到同一块物理内存空间中,当client端与server 端发送消息时,client作为数据发送端,先从自己的进程空间把ipc 通信数据拷贝到内核空间,而server 作为数据的的接收端与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量即可获取内存地址,整个过程只发生一次内存拷贝。这就是他的高效所在。

  那直接将数据的发送端的数据和数据的接收端直接映射到一块物理空间,岂不是更加高效,这与标准linux 标准的共享内存的IPC 机制,就没有区别了,使用共享内存只需要0次拷贝那不更加高效,但是对于多进程的同步问题比较复杂,android 出于基本速率和安全性的考虑选择使用binder 进程跨进程通信。所以说处于综合考虑说binder 是一种高效的进程间通信方式。

 

到此android binder 机制概述 就完了,关于binder 牵扯面比较广,也比较复杂,为此我也是准备和学习了两个星期才将文章写完,中间险些流产了,这些完全是我自己对于binder 的学习和理解,如果有那块出现理解偏差欢迎一起来探讨。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章