rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • Binder机制

Binder机制

什么是进程间通信

进程间通信 IPC (Inner-Process Comunication),就是指不同进程之间的信息传递。

进程是系统进行资源分配和调度的基本单位,是操作系统的结构的基础。

一个应用至少有一个进程,一个进程中有包含了多个线程(线程是CPU调度的最小单位),进程相当于是线程的ViewGroup,线程相当于操作系统分配给进程的View。

什么是 Binder

Binder 是 Android 系统中特有的跨进程通信(IPC)机制,它是一种高效的进程间通信方式,也是 Android 系统的核心技术之一。Binder 不仅是一种 IPC 机制,还是一种 RPC(远程过程调用 Remote Procedure Call)机制,允许一个进程调用另一个进程中的方法,就像调用本地方法一样简单。

Binder是Android系统中一种基于C/S架构的IPC机制,允许不同进程间进行通信。

这种机制的核心在于其独特的驱动层实现,它简化了进程间通信的复杂性,使得跨进程通信看起来就像是进行本地方法调用一样简单。

在Binder机制中,存在服务端和客户端两个主要角色。

服务端会实现一个Binder对象,该对象包含了服务端可以提供给客户端调用的方法接口。然后,服务端通过ServiceManager将这个Binder对象注册为一个服务,这样客户端就可以通过ServiceManager查询到这个服务,并获取到Binder对象的代理(Proxy)。

客户端通过这个代理对象,就可以像调用本地方法一样调用服务端提供的方法,从而实现进程间的通信。

Binder机制的工作原理可以概括为以下几个步骤:

首先,服务端创建并实现Binder对象,定义好可以提供给客户端调用的方法接口;

其次,服务端通过ServiceManager注册服务,将Binder对象暴露给客户端;

接着,客户端通过ServiceManager查询所需服务,并获取到Binder对象的代理;

最后,客户端通过代理对象调用服务端的方法,完成进程间通信。

Binder机制在Android系统中的应用非常广泛,它不仅是应用进程与系统服务进程进行通信的主要方式,还被广泛应用于应用进程之间的通信。

通过Binder机制,Android系统能够实现各种复杂的功能和服务,如活动管理、窗口管理、包管理等。同时,Binder机制也提供了丰富的接口和灵活的使用方式,使得开发者能够轻松地实现跨进程通信,提升应用的性能和用户体验。

虽然Binder机制使得跨进程通信变得简单高效,但在使用过程中也需要注意安全性和稳定性问题。例如,服务端需要对客户端的调用进行权限验证,防止未经授权的访问;同时,也需要处理可能出现的异常情况,确保通信的稳定性。

Binder是Android系统中一种非常重要的IPC机制,它通过独特的驱动层实现简化了进程间通信的复杂性,并提供了丰富的接口和灵活的使用方式。在Android系统的开发和应用中,Binder机制发挥着举足轻重的作用,是实现各种复杂功能和服务的基础。

  • 应用层:是一个能发起通信的Java类。
    • Client:是对 Binder 代理对象,是 Binder 实体对象的一个远程代理。
    • Server:是 Server 中的 Binder 实体对象。
  • 机制:是一种进程间通信机制。
  • 驱动:是一个虚拟物理设备驱动

startActivity的简图:

这里就用到了 Binder 通信,你会发现这里还有 Socker 通信,那我为什么要用 Binder 而不用 Socket。

Android 中 IPC 的方式

名称特点使用场景
Bundle只能传输实现了序列化或者一些Android支持的特殊对象适合用于四大组件之间的进程交互
文件不能做到进程间的即时通信,并且不适合用于高并发的场景适合用于SharedPreference以及IO操作
ContentProvider可以访问较多的数据,支持一对多的高并发访问,因为ContentProvider已经自动做好了关于并发的机制适合用于一对多的数据共享并且需要对数据进行频繁的CRUD操作
Socket通过网络传输字节流,支持一对多的实时通信,但是实现起来比较复杂适合用于网络数据共享
Messenger底层原理是AIDL,只是对其做了封装,但是不能很好的处理高并发的场景,并且传输的数据只能支持Bundle类型多进程、单线程且线程安全
AIDL功能强大,使用Binder机制,支持一对多的高并发实时通信,但是需要处理好线程同步一对多并且有远程进程通信的场景

Binder 优势

Binder机制在Android系统中相比其他传统的IPC方式,如管道、消息队列、套接字等,展现出了显著的优势。这些优势主要体现在性能、安全性和易用性三个方面。

从性能方面来看,Binder采用了共享内存的方式进行数据传输。这意味着在不同进程之间传递数据时,不需要像传统IPC方式那样进行频繁的数据拷贝,从而大大减少了通信开销,提高了通信速度。这种高效的数据传输方式使得Binder在处理大量数据或高频次通信场景时具有显著优势。

在安全性方面,Binder机制通过严格的权限控制和身份验证机制来确保通信双方的安全性。在Android系统中,每个应用程序都有其独特的UID和相应的权限设置。当应用程序尝试通过Binder进行跨进程通信时,系统会对其权限进行检查,确保只有具备相应权限的应用程序才能进行通信。这种权限控制机制有效地防止了恶意应用程序对系统的攻击和破坏,提高了系统的整体安全性。

Binder还提供了丰富的功能扩展性。它支持远程方法调用(RPC)和异步通信等多种通信模式,使得开发者能够根据需要灵活选择适合的通信方式。同时,Binder还支持进程间的死亡通知机制,即当一个进程意外终止时,与其进行通信的其他进程会收到相应的通知,从而能够及时采取相应的处理措施。这种灵活性和可靠性使得Binder在复杂的应用场景中表现出色。

Binder机制在通信速度、安全性和功能扩展性方面相比其他IPC方式具有明显优势。这些优势使得Binder成为Android系统中不可或缺的进程间通信机制,为Android应用的稳定运行和高效交互提供了有力保障。

出发点Binder共享内存Socket
性能拷贝一次无需拷贝拷贝两次
易用性基于C/S架构,易用性高控制复杂,易用性差基于C/S架构,通用接口,传输效率低、开销大
安全每个APP分配UID,同时支持实名和匿名依赖上层协议,访问接入点是开放的不安全依赖上层协议,访问接入点是开放的不安全

通过以上对比,Android 最终选择了自建一套兼顾好用、高效、安全的 Binder。

好用:基于C/S架构,易用性高

高效:用 mmap() 进行内存映射,只需一次拷贝

安全强:每个 APP 分配UID(进程的身份证号),同时支持实名(系统服务)和匿名(自己创建的服务)

可以让自己的服务前往 ServiceManager 注册,注册后实名。

Binder 在系统架构中的作用

Binder与Android系统架构的关联

Binder作为Android系统中的核心组件,其重要性不仅体现在支撑系统架构的稳固性上,更在于它实现了Android系统中各组件之间的高效通信。这种高效的通信方式是Android系统正常运行和实现各种功能的基础。

在Android系统中,四大组件(Activity、Service、BroadcastReceiver、ContentProvider)之间的交互都离不开Binder的支持。例如,当一个Activity需要启动另一个Activity或者调用某个Service时,它们之间的通信就是通过Binder实现的。Binder机制使得这些组件之间的调用看起来像是本地方法调用一样简单,但实际上它们可能运行在不同的进程中。

Binder还实现了Android系统中的跨应用通信功能。在Android系统中,每个应用都运行在自己的进程中,这就需要一种机制来实现不同进程之间的通信。Binder正是这样一种机制,它允许不同应用之间通过接口定义语言(AIDL)定义通信接口,并通过Binder驱动进行通信。这种通信方式是安全的,因为Binder机制内置了权限控制和身份验证,确保只有经过授权的应用才能访问特定的服务或数据。

Binder在系统架构中的关联还体现在其对系统性能的优化上。由于Binder采用了共享内存的方式进行数据传输,这大大减少了数据拷贝的开销,从而提高了通信效率。同时,Binder还支持异步通信模式,这意味着客户端可以在不阻塞主线程的情况下与服务端进行通信,进一步提升了系统的响应速度和用户体验。

总的来说,Binder与Android系统架构的关联是多方面的。它不仅实现了系统组件之间的高效通信,还确保了跨应用通信的安全性和效率。这些功能共同构成了Android系统稳定、高效运行的基础。

Binder底层实现机制简析

深入探索Binder的底层实现机制,我们不难发现其精妙之处。这一机制的核心在于其巧妙地分离了驱动层与用户层,使得复杂的进程间通信操作得以在驱动层中被妥善封装,从而为用户层提供了清晰、直接的接口。

在驱动层,Binder的运作依托于一系列精细设计的数据结构和算法。其中,最为关键的是Binder驱动,它扮演了通信枢纽的角色,负责协调和管理所有Binder通信。通过精心构建的数据结构,如Binder线程池、Binder事务队列等,Binder驱动确保了数据在进程间的有序、高效传输。同时,配合精密的算法控制,如线程调度算法、事务处理算法等,Binder驱动能够实时响应各类通信请求,保证通信的实时性和可靠性。

在用户层,Binder机制的魅力则体现在其代理模式的应用上。这一模式在客户端和服务端之间建立了一种透明的通信桥梁,使得远程方法调用如同本地调用一般简单便捷。具体而言,当客户端需要调用服务端的方法时,它实际上是通过Binder驱动发送一个请求到服务端,并等待服务端的响应。这个过程中,客户端所看到的只是一个代理对象(Proxy),它代表了服务端的那个真实对象。通过这个代理对象,客户端可以像调用本地方法一样调用服务端的方法,而无需关心底层的通信细节。

这种驱动层与用户层的分离设计,以及代理模式的巧妙运用,共同构成了Binder底层实现机制的精髓。它们不仅确保了Binder通信的高效性和安全性,还极大地简化了开发者的使用难度。开发者只需关注于业务逻辑的实现,而无需深陷复杂的进程间通信细节之中。这无疑是Binder机制在Android系统中得以广泛应用的重要原因之一。

Binder底层实现机制还具备很强的可扩展性和灵活性。它可以根据不同的应用场景和需求进行定制和优化,以满足各种复杂的通信需求。例如,在需要高性能通信的场景中,可以通过优化数据结构和算法来提高通信速度;在需要高安全性的场景中,则可以通过加强身份验证和权限控制来确保通信的安全性。这种强大的可扩展性和灵活性使得Binder机制能够不断适应Android系统的发展变化,始终保持其在进程间通信领域的领先地位。

Linux 传统的 IPC 原理

IPC

了解 Linux IPC 相关的概念和原理有助于我们理解 Binder 通信原理。因此,在介绍 Binder 跨进程通信原理之前,我们先聊聊 Linux 系统下传统的进程间通信是如何实现。

Linux 系统基础

  • 进程 / 线程模型、用户态与内核态
  • 系统调用(syscall)机制
  • 内存映射(mmap)原理
  • 驱动程序基本概念

基本概念

由上图看出:

进程隔离。

进程空间划分:用户空间(User Space)/内核空间(Kernel Space)。

系统调用:用户态/内核态。

进程隔离

操作系统中,进程与进程间内存是不共享的。SCC 进程无法直接访问 Service 进程的数据。SCC 进程和 Service 进程之间要进行数据交互就得采用进程间通信(IPC)。

进程空间划分

现在操作系统都是采用的虚拟存储器。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。

内核空间(Kernel Space):是系统内核运行的空间;

用户空间(User Space):是用户程序运行的空间。

所有内核空间(虚拟地址)都映射在同一块物理内存,这样就实现了内存共享(所有进程可通过IPC访问)。

为了保证安全性,它们之间是隔离的。即使用户程序蹦了,内核也不受影响。

系统调用

进程内 用户空间 & 内核空间 进行交互 需通过系统调用,主要通过函数:

copy_from_user():将用户空间的数据拷贝到内核空间;

copy_to_user():将内核空间的数据拷贝到用户空间。

用户态:当进程在执行用户自己的代码的时候,我们称其处于用户运行态(用户态);

内核态:当一个进程执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态)。

系统调用是用户空间访问内核空间的唯一方式。

传统 IPC 通信原理

如图,这就是 Sokcet的拷贝两次。

当然目前 Linux 已经引入 Binder 通信机制。

Binder IPC原理

Binder 采用分层架构设计

应用层: 对于应用通过调用startActivity()然后调用 AMP.startService , 经过层层调用,最终必然会调用到AMS.startService。

Framework: 客户类BinderProxy和服务类Binder(Binder通信是采用C/S架构, Android系统的基础架构便已设计好Binder在Java )。Java 层的 Binder 类、AIDL 生成的 Proxy/Stub 等

Native层: 对于Native层,可以直接使用BpBinder和BBinder(当然这里还有JavaBBinder)即可, 对于上一层Framework 的通信也是基于这个层面。C++ 层的 Binder 实现,如 IBinder、BpBinder、BBinder 等

Kernel: 这里是Binder Driver, 前面3层都跑在用户空间,对于用户空间的内存资源是不共享的,每个Android的进程只能运行在自己进程所拥有的虚拟地址空间, 而内核空间却是可共享的. 真正通信的核心环节还是在Binder Driver。

Binder 驱动

在 Android 系统中,这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)。

定义:一种设备虚拟驱动(内核模块)

作用:负责各个用户进程通过Binder实现进程间通讯

实现原理:内存映射

这是 Binder 驱动最核心的功能:

  • 事务管理:接收客户端(Client)的通信请求(通过 transact() 发起的事务),解析请求目标,转发给对应的服务端(Server)进程。
  • 请求排队:当多个进程同时请求同一服务时,驱动会将请求按顺序放入服务端的事务队列,由服务端的 Binder 线程池依次处理。
  • 同步 / 异步处理:支持同步(客户端等待结果)和异步(客户端不阻塞)两种通信模式,通过事务 flags 区分并调度。

Binder 驱动的核心作用

  • 管理进程间的 Binder 引用计数
  • 实现进程间的数据传递与转发
  • 进行进程身份验证(UID/PID 校验)
  • 维护 Binder 实体与引用的映射关系

Binder IPC 内存映射

Binder IPC 正是基于内存映射(mmap)来实现的,一次完整的 Binder IPC 通信过程通常是这样:

  1. Binder 驱动在内核空间创建一个数据接收缓存区;

  2. 在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系;

内核中数据接收缓存区和接收进程用户空间地址的映射关系;

  1. 发送数据完成了一次进程间的通信。 发送方进程通过系统调用 copy_from_user() 将数据 拷贝 到内核中的内核缓存区;

由于内核缓存区和数据接收缓存区存在内存映射,因此也就相当于把数据发送到了数据接收缓存区;

由于数据接收缓存区和进程的用户空间存在内存映射因此也就相当于把数据发送到了接收进程的用户空间。

内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。

Binder传值限制:

原来的 BINDER_VM_SIZE:((1 * 1024 * 1024) - 4096 * 2)

现在的BINDER_VM_SIZE:((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)

sysconf(_SC_PAGE_SIZE):这个函数用来获取系统执行的配置信息。例如页大小、最大页数、cpu个数、打开句柄的最大个数等等。

这个值表示你Binder最多传这么多,超出就失败。

Android Binder 原理图

Bind 原理图

Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager 以及 Binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。

此处的ServiceManager是指Native层的ServiceManager(C++),并非指framework层的ServiceManager(Java) 原因:

所以,原理图可表示为以下:

Bind 原理图交互

Client、Server、ServiceManager属于进程空间的用户空间,不可进行进程间交互(下图虚线表示)。

所以他们都通过与 Binder 驱动 进行交互的,从而实现IPC通信方式。

所以,原理图可表示为以下:

Bind 原理图交互路线

Binder 涉及的核心技术

1. Binder 驱动

Binder 驱动是整个 Binder 机制的核心,运行在内核空间,负责:

  • 进程间的数据传递
  • 进程标识和验证
  • 引用计数管理
  • 跨进程请求的转发

它是通过内存映射(mmap)实现的,这使得 Binder 通信比传统的 IPC 机制(如管道、Socket)更高效。

2. Binder 实体与引用

  • Binder 实体:服务端中真正提供服务的对象,每个实体都有一个唯一的句柄
  • Binder 引用:客户端持有对服务端实体的引用,通过这个引用实现对远程对象的操作

3. ServiceManager

ServiceManager 是 Binder 机制的 "服务管家",负责:

  • 管理系统中所有的 Binder 服务
  • 提供服务注册(addService)和查询(getService)功能
  • 作为 Binder 通信的上下文管理者

4. Binder 通信模型

Binder 采用了客户端 - 服务端(Client-Server)模型:

  • 服务端:提供服务的进程,实现 Binder 接口
  • 客户端:使用服务的进程,通过 Binder 引用访问服务
  • ServiceManager:服务注册与查询的中间枢纽(类似 DNS)
  • Binder 驱动:核心调度者,负责进程间通信的建立与数据传输

5. AIDL(Android Interface Definition Language)

AIDL 是一种接口定义语言,用于自动生成 Binder 通信的模板代码,简化了跨进程通信的实现。通过 AIDL,开发者可以专注于业务逻辑,而不必关心底层的 Binder 通信细节。

6. Binder 线程池

每个进程都有一个 Binder 线程池,用于处理来自其他进程的 Binder 请求:

  • 服务端通过线程池并发处理多个客户端请求
  • 客户端通过线程池处理服务端的回调

Binder 跨进程通信的实现机制

Binder 基于内存映射(mmap) 实现高效数据传输,核心是「一次拷贝」机制:

  1. Client 进程将数据写入用户空间缓冲区
  2. Binder 驱动通过 mmap 将 Client 缓冲区映射到内核空间
  3. Server 进程直接从内核空间读取数据(无需二次拷贝)

相比之下:

  • Socket:需要 4 次数据拷贝(用户→内核→内核→用户)
  • 管道:需要 2 次数据拷贝(用户→内核→用户)

Binder 通信流程

以 Client 调用 Server 的 add(int a, int b) 方法为例:

  1. 服务注册阶段:
    • Server 实现 AIDL 接口(Stub)
    • 通过 ServiceManager.addService("com.example.Calculator", service) 注册服务
  2. 服务发现阶段:
    • Client 通过 ServiceManager.getService("com.example.Calculator") 获取 Binder 引用
    • 得到的是一个 Proxy 对象(代理)
  3. 方法调用阶段:
    • Client 调用 Proxy 的 add(a, b) 方法
    • Proxy 将参数打包到 Parcel(序列化)
    • 通过 Binder 驱动发送到 Server 进程
    • Server 的 Stub 接收数据并反序列化
    • 执行实际的 add 方法并返回结果
    • 结果通过 Binder 驱动传回 Client

Binder 安全性

  • UID/PID 验证:Binder 驱动会验证通信双方的身份
  • 权限声明:通过 android:permission 限制服务访问
  • 沙箱机制:每个应用运行在独立进程,权限隔离
  • 显式 Intent:跨进程启动服务需使用显式 Intent(Android 5.0+)

Binder 驱动是 Android 安全机制的重要环节

  • UID/PID 校验:通信时自动获取并验证双方进程的 UID(用户 ID)和 PID(进程 ID),确保服务端可识别客户端身份。
  • 权限检查:根据预设的权限规则(如 AndroidManifest 中声明的权限),在事务转发前验证客户端是否有权访问服务,拒绝无权限的请求。
  • 沙箱隔离:通过进程隔离机制,确保恶意应用无法直接访问其他进程的内存或服务,强化系统安全性。

1. 基于 UID/PID 的身份验证机制

Binder 驱动在通信过程中会强制验证参与通信的进程身份,确保服务端能准确识别客户端的来源:

  • 身份标识获取:当进程通过 Binder 驱动发起通信时,驱动会通过内核接口(如 current->cred)获取进程的 UID(用户 ID)和 PID(进程 ID),这些信息是进程的唯一身份标识。
  • 身份传递:在 binder_transaction 事务处理中,驱动会将发送方的 UID/PID 封装到事务数据中(binder_transaction_data 结构体的 sender_euid 和 sender_pid 字段),传递给接收方。
  • 服务端验证:服务端可以通过 Binder.getCallingUid()、Binder.getCallingPid() 等接口获取客户端的身份,并根据身份决定是否处理请求(例如系统服务只允许特定 UID 的进程访问)。

2. 权限检查与访问控制

Binder 驱动提供了细粒度的权限控制机制,确保进程只能访问其被授权的服务:

  • 权限声明与校验:服务端可以在注册服务时声明所需的权限(如通过 addService 时指定 permission 参数),客户端必须在 AndroidManifest 中声明对应的权限才能访问该服务。
  • 驱动层权限验证:当客户端请求访问服务时,Binder 驱动会调用 security_binder_transaction 函数(Linux 安全模块接口),检查客户端是否拥有访问该服务的权限。若权限不足,驱动会直接拒绝事务,返回 PERMISSION_DENIED 错误。
  • 动态权限检查:服务端在处理请求时(如 onTransact 方法中),可通过 checkCallingPermission() 主动校验客户端权限,进一步强化访问控制。

3. 实体与引用的隔离机制

Binder 驱动通过抽象层隔离了进程间的直接内存访问,避免恶意进程篡改数据:

  • 实体隐藏:服务端的 Binder 实体(binder_node)在内核中维护,客户端无法直接获取实体的内存地址,只能通过驱动分配的引用句柄(binder_ref.desc)间接访问。
  • 引用权限绑定:每个 Binder 引用(binder_ref)都与特定进程绑定,其他进程无法复用该引用,防止权限盗用。
  • 内存访问控制:尽管 Binder 通过 mmap 实现内存共享,但驱动会严格限制进程对共享内存的访问范围(仅允许访问自身分配的缓冲区),防止越界读写。

4. ServiceManager 的安全管控

ServiceManager 作为 Binder 服务的 "注册表",其自身的安全由驱动直接保障:

  • 唯一上下文管理者:驱动中通过 binder_become_context_manager() 函数指定唯一的 ServiceManager 进程(通常是 servicemanager 守护进程),其他进程无法冒充。
  • 注册权限控制:只有拥有 android.permission.ADD_SYSTEM_SERVICE 权限的进程(通常是系统进程)才能向 ServiceManager 注册服务,普通应用无法注册系统级服务。
  • 服务查询限制:部分敏感服务(如 ActivityManagerService)的查询需要特定权限,ServiceManager 会在驱动层的协助下验证客户端权限后才返回服务引用。

5. 恶意行为防护

Binder 驱动还针对常见的恶意攻击场景设计了防护机制:

  • 事务大小限制:驱动通过 binder_alloc_buf 函数限制单个事务的数据大小(默认上限为 1MB),防止恶意进程发送超大数据导致内存耗尽(DoS 攻击)。
  • 引用计数与内存回收:通过 binder_inc_ref/binder_dec_ref 维护引用计数,确保 Binder 实体和引用在不再使用时被及时回收,防止内存泄漏和引用劫持。
  • 死锁检测:驱动会监控事务的嵌套调用(通过 transaction_stack),当检测到可能的死锁(如进程间循环等待)时,会通过超时机制中断事务。

总结

Binder 驱动的安全机制是一个多层次、协同工作的体系:

  • 底层通过 UID/PID 实现身份识别,确保 "谁在通信";
  • 中层通过权限校验控制访问范围,确保 "能访问什么";
  • 高层通过实体隔离和 ServiceManager 管控,确保 "通信不被篡改"。

这种设计既满足了 Android 系统对跨进程通信灵活性的需求,又通过内核级别的安全控制,为整个系统的稳定性和安全性提供了坚实保障。

线程管理与调度

Binder 驱动协调进程内 Binder 线程的工作:

  • 线程池维护:监控服务端进程的 Binder 线程(binder_thread)数量,当请求过多时,通知进程创建新线程处理(避免阻塞)。
  • 线程休眠与唤醒:当没有事务处理时,将 Binder 线程置于休眠状态(节省资源);有新事务时,及时唤醒线程处理。
  • 死锁避免:通过事务栈(transaction_stack)跟踪嵌套调用,防止进程间循环等待导致的死锁。

服务管理的底层支持

为 ServiceManager(服务大管家)提供核心支撑:

  • 上下文管理:驱动中存在一个特殊的 Binder 实体(context_manager),作为 ServiceManager 的内核级标识,所有服务注册 / 查询请求都需经过它转发。
  • 服务注册表:协助 ServiceManager 维护服务名称与 Binder 实体的映射关系,确保客户端能通过名称正确找到服务。

Binder 相关的重要类

  • IBinder:Binder 接口基类,定义了跨进程通信的基本方法
  • Binder:实现了 IBinder 接口,是 Server 端的基类
  • Stub:AIDL 自动生成的抽象类,继承自 Binder
  • Proxy:AIDL 自动生成的代理类,实现了接口方法
  • ServiceManager:服务管理类,提供服务注册与查询
  • Parcel:数据打包工具,用于序列化 / 反序列化

AIDL

AIDL定义及作用

AIDL(Android Interface Definition Language)作为Android系统中的重要组成部分,为开发者提供了一种便捷的方式来定义和实现跨进程通信(IPC)的接口。通过AIDL,开发者可以明确地定义服务端所提供的服务接口,包括方法名、参数类型、返回类型等,从而确保客户端和服务端在通信过程中的一致性和准确性。

AIDL的主要作用在于简化了跨进程通信的复杂性。在Android系统中,由于每个应用都运行在自己的进程中,因此不同应用之间的通信需要借助IPC机制来实现。而AIDL正是为了解决这一问题而诞生的。它允许开发者以一种类似于定义本地接口的方式来定义跨进程通信的接口,然后通过编译工具自动生成相应的Binder客户端和服务端代码。这样,开发者就无需关心底层的通信细节,只需关注业务逻辑的实现即可。

在实际应用中,AIDL的使用场景非常广泛。例如,当一个应用需要调用另一个应用提供的服务时,就可以通过AIDL来定义和实现相应的服务接口。此外,AIDL还常用于实现系统级的服务,如电话服务、短信服务等,这些服务需要在不同的应用之间共享和调用。

通过AIDL与Binder的结合应用,Android系统实现了一种高效、安全且易用的跨进程通信机制。这种机制不仅保证了系统各组件之间的紧密联系和协同工作,还为开发者提供了一种灵活的通信方式,以满足各种复杂的应用需求。同时,由于AIDL的引入,使得跨进程通信的接口定义更加规范和统一,提高了代码的可读性和可维护性。

虽然AIDL为跨进程通信提供了极大的便利,但在使用过程中也需要注意一些问题。例如,由于跨进程通信涉及到不同进程之间的数据交换,因此需要确保通信双方的数据格式和协议的一致性。此外,还需要注意权限控制和安全性问题,以防止恶意应用利用跨进程通信进行攻击或窃取数据。

总的来说,AIDL作为Android系统中的一种重要工具,为跨进程通信的实现提供了有力的支持。通过与Binder机制的紧密结合,AIDL使得跨进程通信在Android系统中变得更加简单、高效和安全。

AIDL 是简化 Binder 开发的接口定义语言

作用

  • 自动生成 Binder 通信的模板代码
  • 定义 Client 与 Server 之间的接口规范
  • 处理基本数据类型和复杂对象的序列化

AIDL 支持的数据类型

  • Java 基本类型(int、long、boolean 等)
  • String、CharSequence
  • List(仅支持 ArrayList,元素需是 AIDL 支持类型)
  • Map(仅支持 HashMap,键值需是 AIDL 支持类型)
  • 实现 Parcelable 接口的自定义对象
  • 其他 AIDL 接口

AIDL使用示例及步骤解析

在Android开发中,AIDL(Android Interface Definition Language)与Binder的结合应用是实现跨进程通信(IPC)的一种重要方式。下面,我们将通过一个具体的示例来详细解析使用AIDL和Binder进行跨进程通信的步骤。

步骤一:定义AIDL接口

我们需要在项目中创建一个AIDL文件,该文件用于定义服务端提供的接口规范。在这个接口中,我们将声明客户端需要调用的方法。例如,我们可以创建一个名为IMyService.aidl的文件,并在其中定义一个简单的接口,如获取服务端的数据或向服务端发送数据。

步骤二:实现服务端

在定义了AIDL接口之后,我们需要在服务端实现这个接口。这通常涉及到创建一个Service类,并在该类中实现AIDL接口中定义的方法。在实现这些方法时,我们需要注意使用@Override注解来标记重写的方法,并确保方法的可见性符合AIDL的要求。此外,我们还需要在服务端的onBind方法中返回Binder对象,以便客户端能够获取到服务的代理。

步骤三:注册服务

当服务端实现完成后,我们需要将其注册到Android系统的ServiceManager中。这样,客户端才能通过ServiceManager查询到该服务,并获取到Binder对象的代理。在注册服务时,我们通常会在Service的onCreate方法中调用ServiceManager.addService方法来完成服务的注册。

步骤四:客户端调用

一旦服务被注册到ServiceManager中,客户端就可以通过ServiceManager查询到所需的服务,并获取到Binder对象的代理。之后,客户端就可以通过这个代理来调用服务端的方法了。在客户端调用服务端方法时,实际上是通过Binder驱动层来进行数据的传输和方法的调用的。

步骤五:处理回调

在某些情况下,服务端的方法可能需要回调客户端的某些操作,比如异步通知等。为了实现这种回调机制,我们可以在AIDL接口中定义一个回调接口,并在客户端实现这个回调接口。然后,服务端在需要时可以通过Binder代理来调用客户端的回调方法。这种回调机制使得客户端和服务端之间的通信更加灵活和高效。

使用 Binder 需注意的问题

线程安全问题

  • Binder 方法调用在服务端的 Binder 线程池中执行,不是主线程
  • 若需要更新 UI,需切换到主线程
  • 多个客户端同时调用可能导致并发问题,需做好同步处理

内存管理

  • Binder 引用计数由驱动自动管理,但需注意避免内存泄漏
  • 跨进程传递大对象可能导致性能问题,应考虑分批传递

权限控制

  • 服务端应做好权限验证,防止未授权访问
  • 可通过 checkCallingPermission() 等方法验证调用者权限

异常处理

  • 跨进程调用可能抛出 RemoteException,必须捕获处理
  • 服务端崩溃会导致客户端调用失败,需有容错机制

数据类型限制

  • AIDL 支持的的数据类型有限,复杂类型需要特殊处理
  • 自定义类型必须实现 Parcelable 接口

死亡通知

  • 当服务端进程意外终止时,客户端应能收到通知并做相应处理
  • 通过 linkToDeath() 和 unlinkToDeath() 实现死亡监听

Binder传值限制

在 Android Binder 机制中,跨进程传递数据存在明确的大小限制,主要与 Binder 驱动的内核缓冲区大小有关。理解这些限制对于避免开发中的异常(如 TransactionTooLargeException)至关重要。

  • 原来的 BINDER_VM_SIZE:((1 * 1024 * 1024) - 4096 * 2)
  • 现在的BINDER_VM_SIZE:((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)

sysconf(_SC_PAGE_SIZE):这个函数用来获取系统执行的配置信息。例如页大小、最大页数、cpu个数、打开句柄的最大个数等等。

Binder 传值的核心限制

Binder 驱动内部有一个固定大小的内核缓冲区(通常为 1MB),用于存储跨进程传递的所有数据(包括序列化后的对象、方法参数、返回值等)。这一限制是所有进程共享的全局限制,而非单个进程或单次通信的独立限制。

具体表现为:

  1. 单次通信的数据量上限:

    单次 Binder 调用中传递的数据(包括参数和返回值)不能超过约 1MB - 8KB(实际可用空间略小于 1MB,因为内核需预留部分空间用于管理信息)。

    若超过此限制,会抛出 TransactionTooLargeException。

  2. 全局缓冲区竞争:

    由于缓冲区是全局共享的,即使单次通信数据未达上限,若多个进程同时进行大数据量通信,也可能因缓冲区耗尽导致失败。

限制的底层原因

  1. 内核资源保护:

    Binder 驱动作为内核模块,需严格控制内存使用,避免单个进程占用过多内核资源导致系统不稳定。

  2. 设计定位:

    Binder 主要用于传递轻量数据(如控制指令、小对象),而非大数据(如图片、视频流)。大数据传递更适合通过文件、Socket 或内存映射(mmap)实现。

  3. 性能考量:

    序列化 / 反序列化大对象会消耗大量 CPU 和内存资源,降低跨进程通信效率,与 Binder 追求高效的设计目标相悖。

常见触发场景与解决方案

1. 常见触发场景

  • 传递大型集合(如包含大量元素的 List)
  • 传递高分辨率图片的字节数组
  • 跨进程传递复杂对象(如包含多个嵌套对象的数据结构)
  • 在 Intent 中携带大量数据(Intent 本质通过 Binder 传递)

2. 解决方案

  • 拆分数据:将大数据拆分为多个小块,分多次传递。
  • 使用文件共享:将数据写入文件,跨进程传递文件路径(需注意权限控制)。
  • 采用内存映射(mmap):通过 MemoryFile 或 SharedMemory 实现跨进程共享内存。
  • 使用 ContentProvider:适合大数据(如图片、数据库数据)的跨进程共享,内部通过文件或缓存机制避免 Binder 直接传值。
  • 压缩数据:对数据进行压缩(如 GZIP),减少传递大小(适用于文本等可压缩数据)。

常见应用场景举例

在Android开发的广阔领域中,Binder机制以其高效、安全的特性,成为了实现进程间通信的重要工具。以下,我们将通过几个具体的应用场景,来进一步揭示Binder在实际应用中的关键作用。

在多媒体应用领域,Binder的身影尤为突出。以音频播放器为例,当用户在界面上操作播放、暂停或调整音量时,这些操作指令需要通过Binder机制传递给底层的音频服务进程。音频服务进程在接收到指令后,会进行相应的处理,并将处理结果通过Binder机制反馈回音频播放器,从而实现了流畅的音频播放体验。这种机制确保了音频播放器与音频服务之间的紧密协作,为用户带来了高质量的音频享受。

在社交应用领域,Binder同样发挥着不可或缺的作用。社交应用通常需要频繁地同步用户信息,如好友列表、聊天记录等。这些同步操作往往涉及到多个进程之间的数据交换。通过Binder机制,社交应用可以轻松地实现用户信息与社交服务之间的同步操作。无论是实时聊天还是离线消息推送,Binder都能提供稳定、可靠的通信支持,确保用户数据的实时性和一致性。

在系统服务层面,Binder的应用更是无处不在。Android系统中的许多核心服务,如Activity管理、窗口管理、电源管理等,都依赖于Binder机制进行通信。以Activity管理为例,当应用启动一个新的Activity时,这个请求需要通过Binder机制发送给ActivityManagerService进程进行处理。ActivityManagerService在接收到请求后,会负责创建和管理新的Activity实例,并通过Binder机制将处理结果反馈回应用进程。这种机制确保了系统服务与应用之间的顺畅交互,为整个系统的稳定运行提供了有力保障。

除了上述场景外,Binder还在许多其他领域发挥着重要作用。例如,在游戏开发中,Binder可以用于实现游戏逻辑与渲染进程之间的通信;在支付应用中,Binder可以确保支付请求与支付服务之间的安全交互。总之,无论是在哪个领域,只要涉及到进程间通信的需求,Binder都能以其独特的优势提供稳定、高效的解决方案。

实例分析:Binder在实际项目中的运用

利用Binder机制来实现客户端与服务端的通信。

我们需要在服务端定义一个AIDL接口,该接口包含了音乐服务需要暴露给客户端的方法,如获取音乐列表、播放音乐、暂停音乐等。然后,我们在服务端实现这些方法,这些方法会操作音乐服务的内部状态,如播放列表、播放状态等。

在实现服务端时,我们还需要将服务注册到ServiceManager中,以便客户端能够查询到该服务。注册服务的过程通常是在服务端的onCreate()方法中完成的。

在客户端,我们可以通过ServiceManager查询到音乐服务,并获取到Binder对象的代理。通过这个代理,我们就可以像调用本地方法一样调用服务端的方法了。例如,我们可以调用获取音乐列表的方法来获取服务端的音乐列表,并展示在客户端的界面上。同样地,我们也可以调用播放音乐、暂停音乐等方法来控制服务端的音乐播放状态。

在这个项目中,Binder机制的优势得到了充分的体现。首先,由于Binder采用了共享内存的方式进行数据传输,因此客户端与服务端之间的通信速度非常快,用户可以几乎实时地看到音乐列表的更新和播放状态的改变。其次,Binder的通信方式是安全的,它通过严格的权限控制和身份验证机制确保了通信双方的安全性。在我们的音乐播放器应用中,只有经过身份验证的客户端才能调用服务端的方法,这有效地防止了恶意软件的攻击。

Binder机制还提供了丰富的功能扩展性。在我们的项目中,除了基本的音乐播放控制功能外,我们还可以利用Binder机制实现更多的高级功能,如音乐推荐、歌词同步显示等。这些功能都可以通过扩展AIDL接口并在服务端实现相应的方法来实现。

HIDL 与 Binder 的关系

  • Android HIDL 文档

    了解 Android 8.0 后引入的 HIDL 如何基于 Binder 实现硬件服务通信。

工具与调试

  1. 调试 Binder 通信的工具

    • adb shell dumpsys activity services:查看系统中注册的 Binder 服务。
    • adb shell lshal:查看 HIDL 服务(基于 Binder 的扩展)。
    • logcat -s Binder:过滤 Binder 相关日志,分析通信过程。
    • adb shell dumpsys binder:查看 Binder 状态
    • cat /proc/binder/proc/<pid>:查看指定进程的 Binder 信息
    • 内核调试:ftrace + binder_trace
  2. Binder 引用计数与内存管理

    • Binder 引用计数机制分析

      理解 Binder 实体与引用的生命周期管理。

      Binder 实体和引用都有完善的引用计数机制,确保资源在不被使用时能够被正确释放,避免内存泄漏。

总结

Binder 是 Android 系统的核心 IPC 机制,它通过独特的内存映射方式实现了高效的跨进程通信。理解 Binder 的工作原理和使用注意事项,对于 Android 开发和系统理解都至关重要。在面试中,Binder 相关问题也经常出现,需要重点掌握其核心原理和实际应用。

资料

Android面试官:说说你对 Binder 驱动的了解?

Android系统篇之—-Binder机制和远程服务调用机制分析

binder机制详解

Android跨进程通信IPC之11——AIDL

Binder相关的类图一瞰Binder全貌

掌握 binder 机制?先搞懂这几个关键类!

Android Binder 原理图文分析

Android Binder框架实现之Binder中的数据结构

android跨进程通信(IPC):使用AIDL

最近更新:: 2025/11/25 23:56
Contributors: luokaiwen, 罗凯文