Binder 是什么?
Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现;OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手,现在 OpenBinder 的作者在 Google 工作,这也为后来的 Binder 。从字面上来解释 Binder 有胶水、粘合剂的意思,顾名思义就是粘和不同的进程,使之实现通信。
Binder 有那么重要吗 ?
作为一个 Android 攻城狮,会不会有这也的疑惑?
- 为什么
Activity间传递对象需要序列化? Activity的启动流程是什么样的?- 四大组件底层的通信机制是怎样的?
AIDL内部的实现原理是什么?- 插件化编程技术应该从何学起?
这些都是因为 Binder 才让无数 Android 小伙伴疑惑重重,想要知道 Android 中血液是如何流动的,就必须正确认识 Binder 。
为什么选中了 Binder 实现 IPC 机制?
Android 内核是基于 Linux 系统, 而 Linux 现存多种进程间 IPC(Inter-Process Communication) 方式
- 管道:在创建时分配一个 page 大小的内存,管道是由内核管理的一个缓冲区,缓存区大小比较有限;
- 消息队列:信息复制两次,额外的 CPU 消耗;不合适频繁或信息量大的通信;
- 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- 套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 信号:不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
为何不直接采用 Linux 现有的进程 IPC 方案呢,难道 Linux 社区那么多优秀人员都没有考虑到有 Binder 这样一个更优秀的方案,还是 google 太过于牛 B ?
这得从各个方面考虑到
性能角度(数据拷次数):
Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次,但共享内存方式一次内存拷贝都不需要;从性能角度看,Binder性能仅次于共享内存。稳定性:
Binder是基于C/S架构的,C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题;从这稳定性角度看,Binder架构优越于共享内存。安全性:
Android作为一个开放性的平台,市场上有各类海量的应用供用户选择安装,因此安全性对于Android平台而言极其重要。对于我们普通用户,绝不希望从App商店下载偷窥隐射数据、后台造成手机耗电等等问题,传统Linux IPC无任何保护措施,完全由上层协议来确保。传统
Linux IPC的接收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份;而Android为每个安装好的应用程序分配了自己的UID,故进程的UID是鉴别进程身份的重要标志;传统IPC只能由用户在数据包里填入UID/PID;另外,可靠的身份标记只有由IPC机制本身在内核中添加。其次传统IPC访问接入点是开放的,无法建立私有通道。从安全角度,Binder的安全性更高。但是
Android就算用了Binder架构,而现如今Android手机的各种流氓软件,不就是干着这种偷窥隐射,后台偷偷跑流量的事吗?没错,确实存在,但这不能说Binder的安全性不好,因为Android系统仍然是掌握主控权,可以控制这类App的流氓行为,只是对于该采用何种策略来控制,在这方面android的确存在很多有待进步的空间,这也是google以及各大手机厂商一直努力改善的地方之一。
Linux 传统 IPC 如何通信的?
了解 Linux IPC 相关的概念和原理有助于理解 Binder 通信原理。因此,在介绍 Binder 跨进程通信原理之前,先聊聊 Liunx 中的几个概念以及 Linux 系统下传统的进程间通信是如何实现。

进程隔离
操作系统中进程隔离指的是,进程与进程间内存是不共享的,也就是一个进程不能直接操作或者访问另一个进程,A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。
进程空间划分:用户空间(User Space)/内核空间(Kernel Space)
现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。
操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。Liunx为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间。
内核空间(Kernel)是系统内核运行的空间,用户空间(User Space)是用户程序运行的空间。为了保证安全性,它们之间是隔离的。每个进程可以通过系统调用进入内核,因此,Linux 内核对系统内的所有进程共享。

系统调用
系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态)。
系统调用主要通过如下两个函数来实现:
1 | copy_from_user() //将数据从用户空间拷贝到内核空间 |
Linux 的 IPC 通信原理

- 消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。
- 然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用
copy_from_user()函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。 - 接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用
copy_to_user()函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输,我们称完成了一次进程间通信。
Linux 的 IPC 通信原理有两个问题:
- 一次数据传递需要经历:用户空间 –> 内核缓存区 –> 用户空间,需要 2 次数据拷贝,这样效率不高。
- 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用API接收消息头来获取消息体的大小,浪费了空间或者时间。
Binder IPC 如何通信的?
跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是 Binder 并不是 Linux 系统内核的一部分,那怎么办呢?
这就得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;
- 模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。
- 它在运行时被链接到内核作为内核的一部分运行。
这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。(在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)。)
那 Binder 是如何做到减少数据拷贝的呢?
这就得说一下 Linux 下的另一个概念:内存映射。
Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法,通常是用在有物理介质的文件系统上的。
比如进程中的用户区域是不能直接和物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘-->内核空间-->用户空间);
通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。这样就可以减少数据拷贝次数,用内存读写取代 I/O 读写,提高文件读取效率,实现用户空间和内核空间的高效互动。
两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
但是 Binder 并不存在物理介质!
因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。
- 这样
Binder驱动在内核空间创建一个数据接收缓存区; - 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
- 发送方进程通过系统方法将数据
copy到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

深入了解
- Binder 设备的初始化过程
Binder设备的初始化过程是在Binder驱动程序的初始化函数binder_init中进行的。它首先在目标设备上创建了一个
/proc/binder/proc目录,每一个使用了Binder进程间通信机制的进程在该目录下都对应有一个文件,这些文件是以进程ID来命名的,通过它们就可以读取到各个进程的Binder线程池、Binder实体对象、Binder引用对象以及内核缓冲区等信息。然后调用函数misc_register来创建一个Binder设备,最后Binder驱动程序在目标设备上创建了一个Binder设备文件/dev/binder,这个设备文件的操作方法列表是由全局变量binder_fops指定的。全局变量
binder_fops为Binder设备文件/dev/binder指定文件打开 (binder_open),映射(binder_mmap),数据操作(binder_ioctl)
- binder_open 打开过程
一个进程在使用
Binder进程间通信机制之前,首先要调用函数open打开设备文件/dev/binder来获得一个文件描述符,然后才能通过这个文件描述符来和Binder驱动程序交互,继而和其他进程执行Binder进程间通信。当进程调用函数
open打开设备文件/dev/binder时,Binder驱动程序中的函数binder_open就会被调用,它会为进程创建一个binder_proc结构体proc,并把它加入到一个全局hash队列binder_procs中。Binder驱动程序将所有打开了设备文件dev/binder的进程都加入到全局hash队列binder_procs中。最后会在目标设备上的/proc/binder/proc目录下创建一个以进程ID为名称的只读文件,通过读取这个文件就可以获得进程PID的Binder线程池、Binder实体对象、Binder引用对象以及内核缓冲区等信息。
- 内存映射过程
进程打开了设备文件
/dev/binder之后,还必须要调用函数mmap把这个设备文件映射到进程的地址空间,然后才可以使用Binder进程间通信机制。设备文件/dev/binder对应的是一个虚拟设备,将它映射到进程的地址空间的目的并不是对它的内容感兴趣,而是为了为进程分配内核缓冲区,以便它可以用来传输进程间通信数据。当进程调用函数
mmap将设备文件/dev/binder映射到自己的地址空间时,Binder驱动程序中的函数binder_mmap就会被调用。
Binder驱动程序最多可以为进程分配4M内核缓冲区来传输进程间通信数据。Binder驱动程序为进程分配的内核缓冲区在用户空间只可以读,而不可以写。
Binder驱动程序为进程分配的内核缓冲区有两个地址,其中一个是用户空间地址,另一个是内核空间地址。进程通过用户空间地址来访问这块内核缓冲区的内容,而Binder驱动程序通过内核空间地址来访问这块内核缓冲区的内容。由于它们是连续的,并且起始地址相差一个固定值,因此,只要知道其中的一个地址,就可以方便的计算出另外一个地址。
Binder驱动程序为进程分配的内核缓冲区即为一系列物理页面,它们分别被映射到进程的用户地址空间和内核地址空间。当Binder驱动程序需要将一块数据传输给一个进程时,它就可以先把这块数据保存在为该进程所分配的一块内核缓冲区中,然后再把这块内核缓冲区的用户空间地址告诉进程,最后进程就可以访问到里面的数据了。这样做的好处便是不需要将数据从内核空间复制到用户空间,从而提高了数据的传输效率。
了解了 Binder IPC 的底层通信原理,再看看实现层面 Binder 架构是如何设计的。
Binder 通信模型
一次完整的进程间通信必然至少包含两个进程,通常我们称通信的双方分别为客户端进程 (Client) 和服务端进程 (Server) ,由于进程隔离机制的存在,通信双方必然需要借助 Binder 来实现。
从组件视角来说,Binder 包含 Client 、Server、ServiceManager 以及 binder驱动 ,其中ServiceManager 用于管理系统中的各种服务

其中 Client(AMP)、Server(AMS)、Service Manager 运行在用户空间,它们之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder Driver 进行交互的,Binder 驱动运行在内核空间。其中 Service Manager 和 Binder 驱动由系统提供是 Android 平台的基础架构,而 Client、Server 由应用程序来实现。Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。( 此处的 Service Manager 是指 Native 层的 ServiceManager(C++),并非指 framework 层的 ServiceManager(Java) )
Client、Server、ServiceManager、Binder 驱动这几个组件在通信过程中扮演的角色就如同 互联网中服务器 (Server)、客户端 (Client) 、DNS 域名服务器 (ServiceManager) 以及路由器 (Binder 驱动) 之前的关系。
通常我们访问一个网页的步骤是这样的:首先在浏览器输入一个地址,如 google.com 然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问 DNS 域名服务器,域名服务器中保存了 google.com 对应的 ip 地址 10.249.23.13,然后通过这个 ip 地址才能访问到 google.com 对应的服务器。

Binder 驱动
Binder驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。ServiceManager
ServiceManager和DNS类似,作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder的名字获得对Binder实体的引用。实名 Binder
注册了名字的
Binder叫实名Binder,就像网站一样除了除了有IP地址以外还有自己的网址。
如何注册服务?
Server 创建了 Binder,并为它起一个字符形式,可读易记的名字,将这个 Binder 实体连同名字一起,以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为 “xx” 的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder ,创建位于内核中的实体节点,以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。
但是!这里有个问题! ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。我们在实现进程间通信的时候需要先用到进程间通信,这… 这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋!
解决方法,Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋。ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 实体,其他进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。
ServiceManger 收到数据后从中 取出名字和引用 填入查找表。
ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用 BINDER_SET_CONTEXT_MGR 命令将自己注册成 ServiceManager 时,Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)。这个 Binder 实体的引用在所有 Client 中都固定为 0, 无需通过其它手段获得。
也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。类比互联网,0 号引用就好比是域名服务器的地址,你必须预先动态或者手工配置好。还有需要注意的是,这里说的 Client 是相对于 ServiceManager 而言的,一个进程或者应用程序可能是提供服务的 Server,但对于 ServiceManager 来说它仍然是个 Client。
怎么获取服务?
Server 向 ServiceManager 中注册了 Binder 以后,Client 就能通过名字获得 Binder 的引用了。Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder 我申请访问名字叫 “xx” 的 Binder 引用。
ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为回复发送给发起请求的 Client。
从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。
Binder 通信过程
- 首先,一个进程使用
BINDER_SET_CONTEXT_MGR命令通过Binder驱动将自己注册成为ServiceManager; Server通过驱动向ServiceManager中注册Binder(Server中的Binder实体),表明可以对外提供服务。驱动为这个Binder创建位于内核中的实体节点,以及ServiceManager对实体的引用,将名字以及新建的引用打包传给ServiceManager,ServiceManger将其填入查找表。Client通过名字,在Binder驱动的帮助下从ServiceManager中获取到对Binder实体的引用,通过这个引用就能实现和Server进程的通信。

Binder 通信中的代理模式
我们已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了,但是还有个问题会让我们困惑。
A 进程想要 B 进程中某个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A 进程 没法直接使用 B 进程中的 object。
一次通信过程简述
跨进程通信的过程都有 Binder 驱动的参与,因此在数据流经 Binder 驱动的时候驱动会对数据做一层转换。
当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。
当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了。
Binder 中的具体调用如下:
在 Client 进程和 Server 进程的一次通信过程中,涉及了四种类型的对象,它们分别是
- 位于
Binder驱动程序中的Binder实体对象(binder_node)和Binder引用对象(binder_ref) - 位于
Binder库中的Binder本地对象(BBinder)和Binder代理对象(BpBinder)
它们的交互过程如下:
- 运行在
Client进程中的Binder代理对象通过Binder驱动程序向运行在Server进程中的Binder本地对象发出一个进程间通信请求,Binder驱动程序接着就根据Client进程传递过来的Binder代理对象的句柄值来找到对应的Binder引用对象。 Binder驱动程序根据前面找到的Binder引用对象找到对应的Binder实体对象,并且创建一个事务(binder_transaction)来描述该次进程间通信过程。Binder驱动程序根据前面找到的Binder实体对象来找到运行在Server进程中的Binder本地对象,并且将Client进程传递过来的通信数据发送给它处理。Binder本地对象处理完成Client进程的通信请求之后,就将通信结果返回给Binder驱动程序,Binder驱动程序接着就找到前面所创建的一个事务。Binder驱动程序根据前面找到的事务的相关属性来找到发出通信请求的Client进程,并且通知Client进程将通信结果返回给对应的Binder代理对象处理。

从这个过程就可以看出,Binder 代理对象依赖于 Binder 引用对象,而 Binder 引用对象又依赖于 Binder 实体对象,最后,Binder 实体对象又依赖于 Binder 本地对象。这样,Binder 进程间通信机制就必须采用一种技术措施来保证,不能销毁一个还被其他对象依赖着的对象。为了维护这些 Binder 对象的依赖关系,Binder 进程间通信机制采用了引用计数来维护每一个 Binder 对象的生命周期。
深入了解
- Binder 本地对象的生命周期
Binder本地对象是一个类型为BBinder的对象,它是在用户空间中创建的,并且运行在Server进程中。
Binder本地对象一方面会被运行在Server进程中的其他对象引用,另一方面也会被Binder驱动程序中的Binder实体对象引用。由于
BBinder类继承了RefBase类,因此,Server进程中的其他对象可以简单的通过智能指针来引用这些Binder本地对象,以便可以控制它们的生命周期。由于
Binder驱动程序中的Binder实体对象是运行在内核空间的,它不能够通过智能指针来引用运行在用户空间的Binder本地对象,因此,Binder驱动程序就需要和Server进程约定一套规则来维护它们的引用计数,避免它们在还被Binder实体对象引用的情况下销毁。如何保证本地对象在
Binder实体对象引用的情况下不被销毁?
Server进程将一个Binder本地对象注册到ServerManager时,Binder驱动程序就会为它创建一个Binder实体对象。接下来,当Client进程通过ServerManager来查询一个Binder本地对象的代理对象接口时,Binder驱动程序就会为它所对应的Binder实体对象创建一个Binder引用对象,接着在使用BR_INCREFS和BR_ACQUIRE协议来通知对应的Server进程增加对应的Binder本地对象的弱引用技术和强引用技术。这样就能保证Client进程中的Binder代理对象在引用一个Binder本地对象期间,该Binder本地对象不会被销毁。当没有任何Binder代理对象引用一个Binder本地对象时,Binder驱动程序就会使用BR_DECREFS和BR_RELEASE协议来通知对应的Server进程减少对应的Binder本地对象的弱引用技术和强引用技术。总结来说,
Binder驱动程序就是通过BR_INCREFS、BR_ACQUIRE、BR_DECREFS和BR_RELEASE协议来引用运行在Server进程中的Binder本地对象的,相关的代码实现在函数binder_thread_read中。
- Binder 实体对象的生命周期
Binder实体对象是一个类型为binder_node的对象,它是在Binder驱动程序中创建的,并且被Binder驱动程序中的Binder引用对象所引用。当
Client进程第一次引用一个Binder实体对象时,Binder驱动程序就会在内部为它创建一个Binder引用对象。例如,当Client进程通过ServerManager来获得一个Service组件的代理对象接口时,Binder驱动程序就会找到与该Service组件对应的Binder实体对象,接着再创建一个Binder引用对象来引用它。这时候就需要增加被引用的Binder实体对象的引用计数。相应地,当Client进程不再引用一个Service组件时,它也会请求Binder驱动程序释放之前为它所创建的Binder引用对象。这时候就需要减少该Binder引用对象所引用的Binder实体对象的引用计数。
- Binder 引用对象的生命周期
Binder引用对象是一个类型为binder_ref的对象,它是在Binder驱动程序中创建的,并且被用户空间中的Binder代理对象所引用。当
Client进程引用了Server进程中的一个Binder本地对象时,Binder驱动程序就会在内部为它创建一个Binder引用对象。由于Binder引用对象是运行在内核空间的,而引用了它的Binder代理对象是运行在用户空间的,因此,Client进程和Binder驱动程序就需要约定一套规则来维护Binder引用对象的引用计数,避免它们在还被Binder代理对象引用的情况下被销毁。这套规则可以划分为
BC_ACQUIRE、BC_INCREFS、BC_RELEASE和BC_DECREFS四个协议,分别用来增加和减少一个Binder引用对象的强引用技术和弱引用技术。相关的代码实现在Binder驱动程序的函数binder_thread_write中。
- Binder 代理对象的生命周期
Binder代理对象是一个类型为BpBinder的对象,它是在用户空间中创建的,并且运行在Client进程中。与
Binder本地对象类似,Binder代理对象一方面会被运行在Client进程中的其他对象引用,另一方面它也会引用Binder驱动程序中的Binder引用对象。由于
BpBinder类继承了RefBase类,因此,Client进程中的其他对象可以简单地通过智能指针来引用这些Binder代理对象,以便可以控制它们的生命周期。由于
Binder驱动程序中的Binder引用对象是运行在内核空间的,Binder代理对象就不能通过智能指针来引用它们,因此,Client进程就需要通过BC_ACQUIRE、BC_INCREFS、BC_RELEASE和BC_DECREFS四个协议来引用Binder驱动程序中的Binder引用对象。前面提到,每一个
Binder代理对象都是通过一个句柄值来和一个Binder引用对象关联的,而Client进程就是通过这个句柄值来维护运行在它里面的Binder代理对象的。具体来说,就是Client进程会在内部创建一个handle_entry类型的Binder代理对象列表,它以句柄值作为关键字来维护它内部所有的Binder代理对象。
- 智能指针
智能指针是一种能够自动维护对象引用计数的技术,它是一个对象而不是一个指针,但是它引用了一个实际使用的对象。正是因为它是一个对象,因此它能够自动地维护实际对象的引用计数。简单来说,就是在智能指针构造时,增加它所引用的对象的引用计数;而在智能指针析构时,就减少它所引用的对象的引用计数。由于智能指针的构造和析构都是自动的,因此,它就很自然的实现了自动的对象引用计数技术。
但是这样并不能解决对象的相互引用问题,于是就需要采取一种稍微复杂的引用计数技术来维护对象的生周期了,这种引用计数技术将对象的引用计数分为强引用计数和弱引用计数两种,其中,对象的生命周期只受强引用计数控制。
Android系统提供了三种类型的C++智能指针,分别为轻量级指针(Light Pointer)、强指针(Strong Pointer)和弱指针(Weak Pointer)。
Binder 的完整定义
- 从进程间通信的角度看,
Binder是一种进程间通信的机制; - 从
Server进程的角度看,Binder指的是Server中的Binder实体对象; - 从
Client进程的角度看,Binder指的是对Binder代理对象,是Binder实体对象的一个远程代理 - 从传输过程的角度看,
Binder是一个可以跨进程传输的对象;Binder驱动会对这个跨越进程边界的对象对一点点特殊处理,自动完成代理对象和本地对象之间的转换。
简单 Demo
感谢:本文收集整理自如下文章
为什么 Android 要采用 Binder 作为 IPC 机制?
Android Binder原理(一)学习Binder前必须要了解的知识点
可能问到的问题
- 进程间通信的方式?
- 多进程间的通信
IPC(InterProcess Communication)- AIDL
(Android Interface Definition Language,Android接口定义语言):适合于多进程多线程,需要开发者自己实现线程安全. - Messenger :基于消息的进程间通信的方式,适合于多进程单线程
- AIDL
- Android开发高级进阶——多进程间通信
- Messenger 和 AIDL 的区别
- 多进程间的通信
- Binder机制的作用和原理?
- 上文 Binder IPC 如何通信?
- 简述IPC?
- 什么是AIDL?如何使用?
AIDL(Android Interface Define Language)是IPC进程间通信方式的一种,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码.- AIDL使用解析
- Android中AIDL的使用详解
- AIDL解决了什么问题?
官方文档: Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL. “只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”
- Android 进程分类?
- 谈谈对进程共享和线程安全的认识?
- android的线程安全
- Android开发中多进程共享数据
- Android中UID机制和共享进程
- 问你线程安全的时候,不止要回答主线程跟子线程之间的切换,还有数据结构处理的线程安全问题,多线程操作同一个数据的一致性问题,等等。







