Binder 机制和源码分析
概述
概念
- Binder 是 Android 系统中的一种 IPC 机制。例如当进程 A 中的 Activity 要向进程 B 中的 Service 通信或者使用它提供的方法/数据,需要依赖 Binder IPC。它的设计哲学是让远程调用看起来像本地调用。当客户端调用一个运行在另一个进程中的服务方法时,客户端代码感知不到跨进程的边界,仿佛在调用本地函数一样。这种透明性是Binder设计的核心目标。
- 与传统的Linux IPC机制(如管道、共享内存、Socket)相比,Binder具有以下优势:
- 性能高效:Binder采用mmap机制,通常可以将进程间调用数据的内存拷贝次数减少到一次;传统的 Socket 在用户态与内核态之间一般需要两次拷贝。
- 安全性高:Binder通过UID/PID进行身份验证,并且支持SELinux策略控制
- 面向对象:Binder将远程对象建模为”引用”,客户端持有引用而非原始对象指针
- 语言无关:通过 AIDL(Android Interface Definition Language)定义接口,工具链自动生成 Java/C++ 两侧的 Stub 和 Proxy 代码,使同一套接口规范可跨语言使用;底层传输采用私有的二进制协议(Parcel 序列化格式),与 Protocol Buffers 是完全不同的机制
- Android系统中,几乎所有系统服务(ActivityManagerService、WindowManagerService、PackageManagerService等)都通过Binder进行通信,应用与系统服务之间的交互、应用与应用之间的交互,都依赖Binder机制。
- 当然也存在部分其他的 IPC 方式,例如 Zygote 通信使用 socket。
分层
- Binder架构分为四个层次,从上到下依次是Framework层、JNI层、Native层和Kernel/Drive层。这种分层设计使得Java应用可以通过Framework层的API访问Native层的Binder实现,而Native层的代码则直接与Kernel层的Binder驱动交互。
- Framework层(Java):提供应用开发接口,ServiceManager.java和Binder.java是应用开发者直接打交道的类
- JNI层:作为Bridge层,将Java层的调用转换为Native层的实现,android_util_binder负责BinderProxy相关转换,AndroidRuntime配合JavaBBinder负责Binder相关转换
- Native层(C++):实现Binder的核心逻辑,BBinder代表服务端,BpBinder代表客户端代理,Service Manager是服务注册中心
- Kernel/Drive层:Binder驱动,运行在内核态,负责实际的数据传输和进程调度
- 可以从各层来理解这个机制的定位:
- 从 IPC 角度来说,Binder 是 Android 中的一种跨进程通信方式,是Android独有;
- 从 Android 驱动层,Binder 是一种虚拟字符设备,它的设备节点通常是
/dev/binder; - 从 Android Native 层,Binder 是创建 Service Manager 以及 BpBinder/BBinder 模型,搭建与 binder 驱动的桥梁;
- 从 Android Framework 层,Binder 是各种 Manager(ActivityManager、WindowManager等)和相应 ManagerService 的桥梁;
- 从 Android APP 层,Binder 是客户端和服务端进行通信的媒介,当 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于 AIDL 的服务。
核心机制
Handle机制
- Handle 是 Binder 系统中用于标识远程对象的核心概念。在同一个 Binder 进程上下文中,每个 Binder 对象都会获得一个对该进程唯一的标识符,客户端通过这个标识符来找到目标对象。
- handle = 0:特指 Service Manager(Context Manager),在单个 Binder 上下文中只有 Service Manager 使用这个值
- handle > 0:其他所有服务的标识,在单个 Binder 上下文中从 1 开始递增分配
- 这种设计使得客户端访问Service Manager时无需通过复杂的查找过程,直接使用handle=0即可。当客户端需要访问其他服务时,首先向Service Manager查询服务的handle值,然后使用该handle进行后续调用。
BBinder与BpBinder
- BBinder(B Binder / Binder Native)代表本地Binder对象,是服务端的基类。当你需要实现一个可以供其他进程访问的服务时,你的类需要继承BBinder并重写onTransact方法来处理来自客户端的请求。BBinder对象在Binder驱动中对应一个binder_node结构。
- BpBinder(Bp Binder / Binder Proxy)代表远程Binder的代理,是客户端使用的对象。当客户端获取到一个服务的引用时,实际上获得的是一个BpBinder对象。这个对象并不包含服务的实际实现,它只是一个”代理人”,负责将客户端的调用请求转发给驱动,由驱动将请求发送到服务进程。
- 两者的关键区别:BBinder是真实的服务端实现,而BpBinder只是一个代理。这种设计遵循了代理模式(Proxy Pattern),使得客户端代码可以像调用本地方法一样调用远程服务。
Parcel数据封装
- Parcel是Binder系统中用于序列化和反序列化数据的容器。当需要在进程间传递数据时,数据首先被封装到Parcel对象中,然后通过Binder驱动传输到目标进程。在目标进程中,接收方从Parcel中解析出原始数据。
- Parcel支持多种数据类型的封装,包括基本类型(int、long、String等)、Binder对象、以及文件描述符。其中Binder对象的传输尤为关键:当传输一个BBinder对象时,Parcel会将其转换为handle;当接收方收到handle后,会创建一个对应的BpBinder对象。
架构
总览
- Binder 采用 C/S 架构,主要包含 Client、Server、ServiceManager 以及 binder 驱动 ,其中 ServiceManager 用于管理系统中的各种服务
- Binder 的通信过程可以概括为:
- 注册服务(addService):Server 要先注册 Service 到 ServiceManager 。
- 获取服务(getService):Client 使用某个 Service 前,从 ServiceManager 根据 Handle 获取相应的 Service 。
- 使用服务:Client 根据得到的 Service 信息建立与 Service 所在的 Server 进程通信的通路,然后就可以直接与 Service 交互。
- Client/Server/Service Manager之间是通过与 Binder驱动 间接进行交互的,从而实现IPC通信。其中 Binder 驱动位于内核空间,Client,Server,Service Manager位于用户空间。
可以和 WebRTC 进行类比,Client 通过 Handle 句柄 (SDP/ICE Candidates 元数据) 向 ServiceManager (Signaling Server) 获取到 Server 提前注册好的服务(Address),通过 ioctl (STUN/TURN)经由 Binder driver (HTTP/WebSocket) 的数据拷贝间接与 Server 进行通信
1 | graph TD |
Drive 层
- Binder driver 是 Android 专用的,但底层的驱动架构与 Linux 驱动一样。binder 驱动以 misc 设备注册,作为虚拟字符设备(一般在
/dev/binder),没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的初始化 (binder_init)、打开 (binder_open)、映射(binder_mmap)和数据操作(binder_ioctl)。进行以上系统调用时都需要从用户态进入内核态。
结构体
binder_proc
- 含义:
binder_proc表示一个打开 Binder 设备并参与 IPC 的用户态进程,在内核中的总控对象。用于管理IPC所需的各种信息,拥有其他结构体的结构体。 - 用途:
- 管理该进程所有 Binder 线程(
threads红黑树); - 管理该进程内可见的远端对象引用(
refs_by_desc/refs_by_node); - 管理该进程的 Binder 映射地址空间(
alloc,对应mmap); - 管理待处理工作队列(
todo)和死亡通知、异步事务等调度状态。
- 管理该进程所有 Binder 线程(
1 | // drivers/android/binder_internal.h |
binder_thread
- 含义:
binder_thread代表当前binder操作所在的线程,表示进程内一个实际参与 Binder 收发的内核线程上下文。 - 用途:
- 记录该线程当前事务栈(同步调用链的回栈依赖它);
- 保存线程私有待办队列(优先于进程队列);
- 跟踪 looper 状态(是否已进入线程池、是否注册、是否需要扩容线程池)。
1 | // drivers/android/binder_internal.h |
binder_node
- 含义:
binder_node是服务端本地 Binder 实体对象在内核的表示。 - 用途:
- 把用户态实体(如
BBinder)映射为内核可路由对象; - 维护强弱引用计数,决定对象生命周期;
- 保存是否允许异步事务等行为属性;
- 作为事务路由目标,被
binder_ref在其他进程中引用。
- 把用户态实体(如
1 | // drivers/android/binder_internal.h |
binder_ref
- 含义:
binder_ref是进程对远端binder_node的代理引用。 - 用途:
- 给客户端提供 handle(
desc); - 在本进程维度维护强弱引用计数;
- 连接本地句柄和远端实体 node,用于事务路由与权限控制。
- 给客户端提供 handle(
1 | // drivers/android/binder_internal.h |
binder_write_read
- 含义:
binder_write_read是用户态与内核态在BINDER_WRITE_READioctl 的描述头。用户空间程序和 Binder 驱动程序交互基本都是通过 BINDER_WRITE_READ 命令,来进行数据的读写操作。 - 用途:
- 一次 ioctl 同时提交待发送命令流(write)并拉取待接收命令流(read);
- 避免频繁陷入内核,提升 IPC 吞吐;
- 通过
*_consumed告知用户态实际处理字节数。
1 | // include/uapi/linux/android/binder.h |
binder_transaction_data
- 含义:
binder_transaction_data是一次事务在用户态/内核态之间传递的核心元数据。 - 用途:
- 指明目标(
handle或ptr)、命令码、flags; - 描述数据区和 offsets 区(用于扁平对象、FD、binder 对象重定位);
- 作为
BC_TRANSACTION/BC_REPLY的有效载荷头。
- 指明目标(
1 | // include/uapi/linux/android/binder.h |
binder_transaction
- 含义:
binder_transaction是内核内部表示的一次在途事务对象,在执行binder_transaction()时创建。 - 用途:
- 连接 from 线程与 to 线程/进程;
- 持有该事务对应的
binder_buffer; - 维护同步调用链(
from_parent/to_parent)以支持 reply 回溯; - 跟踪优先级继承、耗时统计、错误状态等。
1 | // drivers/android/binder_internal.h |
binder_buffer
- 含义:
binder_buffer是进程 Binder 映射区中的单笔事务内核缓冲块描述符。每一次 Binder 传输数据时,都会先从 Binder 内存缓存区中分配一个 binder_buffer 来存储传输数据。每一个 binder_buffer 分为空闲和已分配的,通过 free 标记来区分。空闲和已分配的 binder_buffer 通过各自的成员变量 rb_node 分别连入 binder_proc 的 free_buffers 和 allocated_buffers 。 - 用途:
- 记录这块内存归属哪个进程、大小和位置;
- 标记是否异步事务占用、是否允许释放;
- 跟踪数据区与对象偏移区,便于复制/回收。
1 | // drivers/android/binder_alloc.h |
方法
binder_init
- 用途:
- 在驱动模块加载时完成全局初始化;
- 注册字符设备(misc/binderfs 设备节点);
- 初始化全局链表、锁、调试项、shrinker 等基础设施。
- 执行流程:
- 初始化全局状态(进程表、统计、锁);
- 注册
binder_fops,暴露open/mmap/ioctl/poll/release; - 初始化调试接口(debugfs 或 binderfs 控制节点);
- 返回可供用户态
open("/dev/binder")使用的驱动入口。
1 | // drivers/android/binder.c |
binder_open
- 用途:
- 打开 binder 驱动设备, 用户态
open("/dev/binder")时创建并初始化binder_proc; - 把当前进程接入 Binder 驱动管理体系。
- 打开 binder 驱动设备, 用户态
- 执行流程:
- 分配
binder_proc, 保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs;Binder 驱动中通过static HLIST_HEAD(binder_procs),创建了全局的哈希链表binder_procs,用于保存所有的binder_proc队列,每次新创建的binder_proc对象都会加入binder_procs链表中。 - 初始化锁、红黑树、待办队列、等待队列;
- 初始化
binder_alloc(但尚未映射,映射在mmap); - 绑定到
file->private_data,后续 ioctl/mmap 都能拿到该进程上下文。
- 分配
1 | // drivers/android/binder.c |
binder_mmap
- 用途:
- 把进程用户态虚拟地址区映射为 Binder 事务缓冲池:
- 首先在内核虚拟地址空间,申请一块与用户虚拟内存相同大小的内存;然后再申请 1 页物理内存,再将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,从而实现了用户空间的 Buffer 和内核空间的 Buffer 同步操作的功能。
- 建立用户态可见地址和内核页的管理关系。
- 把进程用户态虚拟地址区映射为 Binder 事务缓冲池:
- 执行流程:
- 校验映射参数(大小、权限、flags);
- 在
binder_alloc中建立地址区元数据; - 分配/预留管理页并设置 VM 操作回调;
- 返回后,进程可在该映射区与内核共享事务数据。
1 | // drivers/android/binder.c |
binder_ioctl
用途:
- Binder 驱动统一控制入口;绝大多数 IPC 控制命令都通过它进入内核;负责在两个进程间收发IPC数据和IPC reply数据。
- 核心命令是
BINDER_WRITE_READ,其内部会触发binder_thread_write与binder_thread_read。
执行流程:
- 依据
cmd分发:设置上下文管理者、线程数限制、版本查询、收发命令等; - 对
BINDER_WRITE_READ:先处理用户写入的 BC_* 命令,再组织 BR_* 返回; - 将结果拷回用户态,驱动一次往返完成发请求 + 收事件/回包。
- 依据
主要的命令:
BINDER_WRITE_READ:收发 Binder IPC 数据。BINDER_SET_MAX_THREADS:设置 Binder 线程池最大线程数。BINDER_SET_CONTEXT_MGR:设置ServiceManager对应的 Context Manager 节点。BINDER_THREAD_EXIT:释放并退出当前 Binder 线程。BINDER_VERSION:获取 Binder 协议版本信息。
对于传递进来的命令是 BINDER_WRITE_READ 时执行 binder_ioctl_write_read:
- 首先,把用户空间数据 ubuf 拷贝到内核空间 bwr ;
- 如果 bwr 写缓存有数据,则执行 binder_thread_write ;当写失败则将 bwr 数据写回用户空间并退出;
- 如果 bwr 读缓存有数据,则执行 binder_thread_read ;当读失败则再将 bwr 数据写回用户空间并退出;
- 最后,把内核数据 bwr 拷贝到用户空间 ubuf 。
binder_get_thread 从 binder_proc 中查找 binder_thread ,如果当前线程已经加入到 proc 的线程队列则直接返回,如果不存在则创建 binder_thread ,并将当前线程添加到当前的 proc。
1 | // drivers/android/binder.c |
binder_transaction
- 用途:
- 把发送方事务请求路由到目标进程/线程;
- 完成对象翻译(binder object / fd)、数据拷贝、工作入队与唤醒。
- 执行流程(核心):
- 根据
handle找到目标binder_ref -> binder_node -> target_proc; - 为目标进程分配
binder_buffer; - 将发送方 Parcel 数据复制到目标缓冲区,并修正对象偏移;
- 构建
binder_transaction,决定投递到目标线程或进程队列; - 唤醒目标线程;若同步调用则挂接事务栈等待 reply。
- 根据
1 | // drivers/android/binder.c |
binder_thread_write
用途:
- 处理用户态写入驱动的 BC_* 命令流;
- 包括发起事务、回复事务、释放缓冲、引用计数变化、线程 looper 状态更新等。
执行流程:
- 循环解析写缓冲中的 BC 命令;
- 对
BC_TRANSACTION/BC_REPLY调用binder_transaction; - 对
BC_FREE_BUFFER释放已完成事务缓冲; - 更新
write_consumed并返回,供用户态继续推进。
BC 请求: binder_thread_write() 根据不同的 BC 协议而执行不同的流程。 其中 BC_TRANSACTION 和 BC_REPLY 协议,会进入 binder_transaction() 。
请求列表:
BC_TRANSACTION:Client 向 Binder 驱动发送事务请求数据。BC_REPLY:Server 向 Binder 驱动发送事务回复数据。BC_FREE_BUFFER:释放已使用完成的事务缓冲区。
Binder 缓冲区由mmap()建立的映射区域提供,驱动在free_buffers与allocated_buffers之间按需分配和回收。Android 14 中普通应用进程(通过ProcessState)的映射大小约为1MB-8KB;ServiceManager 同样通过ProcessState::initWithDriver()初始化,映射大小也是1MB-8KB(旧版 C 实现的 ServiceManager 才使用 128KB)。应用处理完成后应尽快发送该命令回收缓冲,避免缓冲区长期占用。BC_INCREFS:binder_ref弱引用+1。BC_DECREFS:binder_ref弱引用-1。BC_ACQUIRE:binder_ref强引用+1。BC_RELEASE:binder_ref强引用-1。- 以上强/弱引用命令用于维护 Binder 对象生命周期(对应强/弱指针语义)。
BC_ACQUIRE_DONE:binder_node强引用处理完成(对应驱动侧收尾,常见为计数回落)。BC_INCREFS_DONE:binder_node弱引用处理完成(对应驱动侧收尾,常见为计数回落)。BC_REGISTER_LOOPER:注册新的 looper 线程(通常对应joinThreadPool()创建的非主 Binder 线程)。BC_ENTER_LOOPER:线程进入 looper(通常对应joinThreadPool()主 Binder 线程进入循环)。BC_EXIT_LOOPER:线程退出 looper(通常是非主 Binder 线程在超时等条件下退出)。BC_REQUEST_DEATH_NOTIFICATION:注册死亡通知。BC_CLEAR_DEATH_NOTIFICATION:取消已注册的死亡通知。BC_DEAD_BINDER_DONE:确认已完成死亡通知处理。
1 | // drivers/android/binder.c |
binder_thread_read
用途:
- 把驱动中的待处理事件转换为用户态可消费的 BR_* 命令;
- 事件包括:收到事务、收到 reply、引用变化、死亡通知等。
执行流程:
- 优先从线程私有
todo取工作,其次取进程todo; - 若无任务且允许阻塞,则睡眠等待唤醒;
- 取到事务后写入
BR_TRANSACTION/BR_REPLY及其数据头到读缓冲; - 返回给用户态,
IPCThreadState再分发到 Java/Native 业务层。
- 优先从线程私有
BR 响应: binder_thread_read() 根据不同的 BR 协议而执行不同的流程。
响应列表:
BR_ERROR:操作发生错误。BR_OK:操作完成。BR_NOOP:空操作(占位响应,不携带业务语义)。BR_SPAWN_LOOPER:要求进程创建并注册新的 looper 线程。
常见触发场景是驱动检测到当前没有可用等待线程来处理即将到来的事务。BR_TRANSACTION:Binder 驱动向 Server 端投递事务请求数据。BR_REPLY:Binder 驱动向 Client 端投递事务回复数据。BR_TRANSACTION_COMPLETE:请求发送完成通知。
例如 Client 发送BC_TRANSACTION后,会收到该响应;Server 发送BC_REPLY后也会收到该响应,表示命令已被驱动接收并完成对应发送流程。BR_DEAD_REPLY:回复失败,常见原因是目标线程或目标节点已不可达(对端可能已死亡)。BR_FAILED_REPLY:回复失败,常见原因是事务处理过程出错(如事务校验/执行失败)。BR_INCREFS:binder_ref弱引用+1(通常作用于 Server 侧本地对象管理)。BR_DECREFS:binder_ref弱引用-1。BR_ACQUIRE:binder_ref强引用+1。BR_RELEASE:binder_ref强引用-1。- 以上用于实现强弱指针。
BR_DEAD_BINDER:Binder 驱动向 Client 端发送死亡通知。BR_CLEAR_DEATH_NOTIFICATION_DONE:BC_CLEAR_DEATH_NOTIFICATION对应的完成响应码。
1 | // drivers/android/binder.c |
通信分析
通信模型
Binder 通信模型本质:用户态代理/实体对象模型 + 内核事务路由模型。
- 用户态(Java/Native)把方法调用封装为 Parcel;
- 内核 Binder 驱动把 Parcel 作为事务在进程/线程 + 对象引用图里转发;
- 服务端线程执行后再通过 reply 原路返回(同步)或仅投递不回包(oneway)。
Client 侧:
BinderProxy/BpBinder负责把方法调用编码为BC_TRANSACTION。IPCThreadState将命令写入binder_write_read.write_buffer并发起ioctl(BINDER_WRITE_READ)。
Kernel 侧:
binder_thread_write解析 BC 命令,遇到事务则进binder_transaction;binder_transaction完成目标定位、缓冲分配、数据复制、入队、唤醒;binder_thread_read把目标侧待办转为BR_TRANSACTION给服务线程。
Server 侧:
- 服务线程从
talkWithDriver()收到BR_TRANSACTION; BBinder::transact/onTransact执行业务;- 同步调用通过
BC_REPLY回写,最终在 Client 读路径收到BR_REPLY。
- 服务线程从
具体流程如下:
- Client 调用代理方法,
BpBinder::transact组装binder_transaction_data(code、flags、data、offsets)。 - Client 线程执行
ioctl(BINDER_WRITE_READ):- 写阶段进入
binder_thread_write; - 识别
BC_TRANSACTION后调用binder_transaction。
- 写阶段进入
binder_transaction在内核中执行关键动作:- 按 handle 查
binder_ref,定位到目标binder_node和target_proc; - 在
target_proc->alloc分配binder_buffer; - 拷贝/翻译数据(尤其 binder 对象与 FD);
- 构造
binder_transaction并入target_thread->todo或target_proc->todo; - 唤醒目标进程 Binder 线程。
- 按 handle 查
- Server 线程在
binder_thread_read中被唤醒,读到BR_TRANSACTION,用户态进入onTransact执行业务逻辑。 - 业务执行完毕后:
- 同步调用:Server 再发
BC_REPLY; - oneway:不发 reply,直接完成并回收资源。
- 同步调用:Server 再发
- 对同步调用,Client 后续 read 阶段进入
binder_thread_read,收到BR_REPLY,transact返回到调用方,整次 RPC 闭环结束。
- Client 调用代理方法,
为什么是 binder_thread_write + binder_thread_read 双阶段
- write 阶段负责提交意图(发事务、回包、释放缓冲、状态更新);
- read 阶段负责提取结果/事件(收到请求、收到回包、死亡通知等);
- 一个
BINDER_WRITE_READ可同时做两件事,减少系统调用次数,并让线程在无事可做时自然阻塞在 read 等待上,实现高效事件驱动。
ServiceManager
- ServiceManager 是 Binder IPC 通信过程中的核心系统服务进程,本身也是一个 Binder 服务,主要用于查询和注册服务。
- 在Binder系统中,所有服务都需要注册到一个”服务注册中心”,客户端才能找到这些服务。Service Manager就是这个注册中心,它在整个Android系统中扮演着类似DNS的角色。
与DNS的工作方式类似,如果没有Service Manager,客户端将不知道服务在哪里。客户端需要预先知道每个服务的地址(这里是handle),这在实际系统中是不可行的。
Service Manager允许服务在启动时注册自己,客户端在需要时向Service Manager查询。Service Manager的特殊性在于:它是Android系统中唯一一个使用固定handle(handle=0)的服务。这样设计是为了让所有进程都能轻易地找到它,而不需要额外的查找机制。
工作流程:
- 打开 binder 驱动,调用 mmap() 方法映射 Binder 缓冲区(Android 14 中通过
ProcessState::initWithDriver()实现,映射大小为BINDER_VM_SIZE = 1MB - 2×PAGE_SIZE ≈ 1MB-8KB,并通过setThreadPoolMaxThreadCount(0)限制线程池规模,以减少 ServiceManager 的系统资源占用); - 通过
ps->becomeContextManager()向内核发送BINDER_SET_CONTEXT_MGR_EXTioctl,在驱动中注册为 Context Manager(即 handle=0 对应的管理节点)。Android 14 中会先尝试BINDER_SET_CONTEXT_MGR_EXT,若内核不支持则回退到BINDER_SET_CONTEXT_MGR;注意这是注册为 Context Manager,不是成为 daemon 进程; - SELinux 权限验证通过
Access类封装,在注册/查询服务时对发起方的 UID/PID 和目标服务名做策略校验; - 进入
Looper::pollAll(-1)主循环,通过BinderCallback(监听 binder fd 上的可读事件)等待并处理来自其他进程的请求。
- 打开 binder 驱动,调用 mmap() 方法映射 Binder 缓冲区(Android 14 中通过
注册服务时同一个服务已注册,重新注册前会先移除之前的注册信息;
当binder所在进程死亡后,会调用binder_release方法,然后调用binder_node_release发出死亡通知
1 | flowchart TB |
启动 Service Manager
- ServiceManager 是由 init 进程通过解析 init.rc 文件而创建的,其所对应的可执行程序 /system/bin/servicemanager ,进程名为 /system/bin/servicemanager。init进程读取init.rc中关于servicemanager的配置, 指定servicemanager运行在system用户下,属于core类服务(这些服务失败会导致系统崩溃)。init进程首先fork创建一个新进程,然后在新进程中execve执行/system/bin/servicemanager程序。
- servicemanager进程打开Binder设备后,调用
BINDER_SET_CONTEXT_MGR_EXT(不支持时回退到BINDER_SET_CONTEXT_MGR)将自己注册为 Context Manager。 - Binder 驱动在处理这个 ioctl 时,会创建一个特殊的
binder_node结构。这个节点有以下特点:- 它在当前 binder context 中唯一,每个 Binder 设备/挂载点只能有一个 Context Manager
- 它与当前 binder context 中的 handle=0 永久绑定,驱动会特殊处理所有 handle=0 的请求
- 它的proc字段指向servicemanager进程的binder_proc结构
- 注册完成后,servicemanager进程进入主循环,通过ioctl BINDER_WRITE_READ等待处理来自其他进程的请求。此时,Android系统的Binder通信基础设施已经完全就绪。
1 | // frameworks/native/cmds/servicemanager/servicemanager.rc (Android 14) |
1 | // frameworks/native/cmds/servicemanager/main.cpp (Android 14) |
1 | sequenceDiagram |
becomeContextManager 与内核注册
ps->becomeContextManager()是 Android 14 中注册 Context Manager 的统一入口,它封装了对内核的 ioctl 调用:
1 | // frameworks/native/libs/binder/ProcessState.cpp |
- 内核侧对应
binder_ioctl_set_ctx_mgr(),其关键逻辑如下: context->binder_context_mgr_node是 per-context(每个binder_device,即每个 binderfs 挂载点)的,而不是全局唯一。
1 | // drivers/android/binder.c |
获取 Service Manager
- 客户端进程需要与 Service Manager 通信时,首先必须获取其代理对象。Android 系统通过
ServiceManager.java提供了高层抽象,隐藏了 handle=0 的底层细节。获取 SM 代理的过程本质上是创建一个指向 SM 进程的BpBinder(0)对象,由于 SM 始终使用固定的 handle=0,这个过程不需要查找,直接构造即可。 - 在 Java 层,获取 SM 代理的入口是
ServiceManager.getIServiceManager()(Android 14),该方法维护一个静态IServiceManager单例,存在时直接返回,不存在时通过BinderInternal.getContextObject()→ JNI →ProcessState::getContextObject()→getStrongProxyForHandle(0)完成创建,最终封装为 Java 层BinderProxy。 ProcessState::init()负责打开/dev/binder驱动并通过mmap()映射大小为BINDER_VM_SIZE = 1MB - 2×PAGE_SIZE的地址空间;初始化时设定当前进程最大并发 Binder 线程数为DEFAULT_MAX_BINDER_THREADS = 15(即最多 15 个额外线程,加上主线程共 16 个)。getContextObject()内部调用getStrongProxyForHandle(0):先检查mHandleToObject缓存,不存在时调用makeBinderProxy(0)构造BpBinder(handle=0)并缓存。返回的BpBinder在 JNI 层经javaObjectForIBinder()包装为 Java 层BinderProxy,再通过ServiceManagerNative.asInterface()或 AIDL 生成的Stub.asInterface()转成IServiceManager接口代理。
1 | // frameworks/native/libs/binder/ProcessState.cpp |
1 | sequenceDiagram |
addService 与 getService
- Android 14 的 C++
ServiceManager类(frameworks/native/cmds/servicemanager/ServiceManager.cpp)维护一张std::map<std::string, Service>服务表,所有注册和查询都通过它进行。 - handle 的产生:
getService()返回的sp<IBinder>在 Parcel 序列化(writeStrongBinder)时,驱动侧会将服务端的binder_node翻译为客户端进程中的一个新binder_ref,并分配对应的handle(desc)。客户端收到BR_REPLY后,Parcel 反序列化将该 handle 包装为BpBinder(handle=N),再通过 JNI 转成 Java 层的BinderProxy,整个过程对调用方透明。
1 | // frameworks/native/cmds/servicemanager/ServiceManager.cpp |
注册 addService
服务注册是让 Service Manager 记录服务名称到服务 Binder 引用映射关系的过程,这张映射表是后续
getService查询的基础。服务进程启动后创建自己的 Binder 实体(BBinder子类),然后以name - IBinder的形式注册到 SM,SM 将其存入mNameToService并监听死亡通知,进程死亡后自动清理条目。注册过程本质上是服务端作为客户端向 SM(handle=0)发起的一次 Binder 调用
Java 层:
ServiceManager.addService(name, binder)→getIServiceManager().addService(...)Native 层:
IServiceManager::addService()→BpBinder(0).transact(ADD_SERVICE_TRANSACTION)→IPCThreadState::talkWithDriver()→ioctl(BINDER_WRITE_READ)驱动层:handle=0 路由到 SM 进程,SM 的
onTransact()被触发SM 侧:
ServiceManager::addService()写入mNameToService,并通过linkToDeath监听服务存活Java 层入口(
frameworks/base/core/java/android/os/ServiceManager.java):
1 | // frameworks/base/core/java/android/os/ServiceManager.java |
1 | sequenceDiagram |
获得 getService
服务获取是服务注册的逆过程。客户端向 SM 查询服务名对应的 Binder 引用,SM 在
mNameToService中查找,找到则将服务端的binder_node通过驱动翻译为客户端进程中的binder_ref(handle=N),返回给客户端。整个流程的核心路径:
- Java 层:
ServiceManager.getService(name)→getIServiceManager().getService(name)→ 通过BinderProxy(0)发送GET_SERVICE_TRANSACTION事务 - 驱动层:handle=0 路由到 SM,SM 的
getService()在mNameToService中查找,将结果中的IBinder(即服务端binder_node)序列化为flat_binder_object(type=BINDER_TYPE_HANDLE)写入 reply - 驱动翻译:驱动在
BR_REPLY传回时,将 SM 进程视角的binder_node引用转换为客户端进程中新分配的binder_ref(handle=N) - 客户端解包:
Parcel::readStrongBinder()→BpBinder(handle=N)→ JNIjavaObjectForIBinder()→ Java 层BinderProxy(handle=N)→ AIDLStub.asInterface()转为具体服务代理
- Java 层:
handle 分配说明:
getService()返回的 handle 是驱动为客户端进程分配的,存储在客户端binder_proc的refs_by_desc红黑树中,与 SM 进程的命名空间完全隔离。不同客户端进程对同一服务可能拿到不同的 handle 值,但都指向同一个binder_node。Java 层入口(
frameworks/base/core/java/android/os/ServiceManager.java):
1 | // frameworks/base/core/java/android/os/ServiceManager.java |
1 | sequenceDiagram |
进程与线程
Binder 驱动在内核中为每个参与通信的进程和线程分别维护对应的内核结构体,并通过线程池机制实现并发处理。
binder_proc 与 ProcessState:驱动通过全局
binder_procs链表记录所有已打开/dev/binder的进程,每个进程对应一个binder_proc结构体。用户空间侧,每个进程有且只有一个ProcessState单例(通过std::call_once保证),它持有 binder fd 和 mmap 映射区,是进程级 Binder 资源的统一入口。binder_thread 与 IPCThreadState:进程中每个 Binder 工作线程在驱动中都有对应的
binder_thread结构体,挂在binder_proc::threads红黑树(rb_root threads)上,以线程 TID 为 key。用户空间侧,每个线程有自己的IPCThreadState实例(thread-local 单例),负责维护与驱动通信的mIn/mOut缓冲区并驱动talkWithDriver()调用。Binder 线程池:每个 Server 进程在调用
startThreadPool()时注册第一个工作线程进入循环;当驱动发现所有线程均忙时,会通过BR_SPAWN_LOOPER命令通知用户空间派生新线程(最多不超过DEFAULT_MAX_BINDER_THREADS = 15个额外线程,加主线程共 16 个)。所有来自 Client 端的 Binder 请求均由 Server 端的线程池处理,Client 调用线程在等待BR_REPLY期间阻塞在ioctl(BINDER_WRITE_READ)上。
IPCThreadState.cpp
IPCThreadState.cpp是 native 层与 binder 驱动之间的直接桥梁。它维护每个 Binder 线程的线程局部状态、命令缓存、事务发送/接收流程,以及线程池生命周期。
每线程状态与生命周期
IPCThreadState::self()使用 pthread TLS 保证每个线程拥有自己的IPCThreadStateselfOrNull()仅在线程已初始化 TLS 时返回当前线程状态,不会创建新对象。shutdown()负责在进程退出时清理 TLS key,并删除当前线程的IPCThreadState- 线程销毁时,
threadDestructor()会先调用flushCommands(),再通过BINDER_THREAD_EXIT告知驱动线程离开,确保挂起命令不会丢失。
1 | IPCThreadState* IPCThreadState::self() |
Binder 线程池与命令循环
joinThreadPool()是服务线程进入 Binder 线程池的入口。它写入BC_ENTER_LOOPER,然后循环调用getAndExecuteCommand()getAndExecuteCommand()调用talkWithDriver()拉取新命令,并将mExecutingThreadsCount增加入当前活跃线程统计。也就是 Binder 线程池饥饿检测- 如果驱动返回
BR_SPAWN_LOOPER,executeCommand()会调用mProcess->spawnPooledThread(false),这是内核通知用户态需要额外线程的标准路径。
1 | ```cpp |
1 | status_t IPCThreadState::getAndExecuteCommand() |
1 |
|
1 | status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, |
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
1 | status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) |
case BR_TRANSACTION:
binder_transaction_data tr;
mIn.read(&tr, sizeof(tr));
Parcel buffer;
buffer.ipcSetDataReference(…);
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
1 |
|
1 |
|
void IPCThreadState::freeBuffer(const uint8_t* data, …)
{
mOut.writeInt32(BC_FREE_BUFFER);
mOut.writePointer((uintptr_t)data);
state->flushIfNeeded();
}
1 |
|
内存管理
- 虚拟进程地址空间和内核映射区通常通过同一块物理内存建立共享映射。当 Client 端与 Server 端发送数据时,Client 先将 IPC 数据拷贝到 binder mmap 的共享缓冲区;Server 端读取时通常不需要再做一次完整拷贝,而是直接访问该共享缓冲区,从而减少了跨进程拷贝次数。
binder_alloc 缓冲分配器
binder_alloc是 Binder 驱动中每进程独立的内存分配器,管理着mmap建立的映射区域,负责为每笔事务分配和回收binder_buffer。映射关系:
binder_mmap→binder_alloc_mmap_handler()建立内核虚拟地址(vma_vm_start)到用户虚拟地址(vma->vm_start)的映射,两者最终映射到同一批物理页。服务端接收数据时,内核直接写到这批物理页;服务端用户态通过mmap地址读取,无需二次拷贝。分配策略:空闲缓冲块以大小为键放在
free_buffers(红黑树),已分配块以地址为键放在allocated_buffers(红黑树),并通过buffers链表按地址顺序串联,便于合并相邻空闲块(类似伙伴系统的紧凑策略)。异步事务配额:
binder_alloc会统计当前异步(oneway)事务占用的缓冲字节总量,超过映射区一半时拒绝新的 oneway 分配,以防 oneway 堆积耗尽缓冲区(这是前述 oneway spam 检测机制的内存层基础)。分配完整流程:
binder_transaction()调用binder_alloc_new_buf(&target_proc->alloc, ...)向目标进程的分配器申请缓冲块;
- 分配器从
free_buffers红黑树中取出最小合适块,必要时向内核页分配器申请物理页并建立页表映射(binder_update_page_range);
- 分配器从
- 事务数据通过
binder_alloc_copy_user_to_buffer()从发送方用户空间写入该缓冲块对应的内核虚拟地址;
- 事务数据通过
- 由于该缓冲块也映射到接收方的用户空间,接收方通过
mmap地址直接读取
- 由于该缓冲块也映射到接收方的用户空间,接收方通过
- 接收方处理完毕后发送
BC_FREE_BUFFER,驱动调用binder_alloc_free_buf()回收缓冲块,必要时归还物理页(通过shrinker在内存紧张时主动回收)。
- 接收方处理完毕后发送
1 | // drivers/android/binder_alloc.c |
Native 层
BBinder 与 BpBinder
- Binder Native 层的核心是
BBinder(服务端本地对象)与BpBinder(客户端代理对象)这对搭档,它们共同实现了代理模式,使远程调用对调用方透明。
BBinder(服务端)
BBinder继承自IBinder,是所有本地 Binder 服务的基类。当一个进程想对外提供 Binder 服务时,就继承BBinder并重写onTransact()方法处理来自客户端的调用请求。BBinder::transact()会先做安全检查,然后调用子类实现的onTransact(),最后通过驱动将结果回传给调用方。- 调用流程:
IPCThreadState从驱动的BR_TRANSACTION中取出事务,调用BBinder::transact()分发到onTransact(), AIDL 生成的 Stub 子类按code路由到具体接口实现,结果写入replyParcel 并通过BC_REPLY回传给调用方。
1 | // frameworks/native/libs/binder/Binder.cpp |
BpBinder(客户端代理)
BpBinder是客户端侧的远程代理,持有目标服务的handle,所有对远程方法的调用都通过它转发给 Binder 驱动。- BpBinder 的生命周期管理:
BpBinder通过ProcessState::getStrongProxyForHandle(handle)获取或创建,内部维护一张handle - BpBinder的弱引用表(mHandleToObject),避免同一 handle 创建多个代理对象。当BpBinder强引用计数归零时,会向驱动发送BC_RELEASE和BC_DECREFS,通知驱动端减少对应binder_ref的引用计数。
1 | // frameworks/native/libs/binder/BpBinder.cpp |
死亡通知
概述
- 死亡通知(Death Notification)是 Binder 提供的一种机制,允许客户端监听服务端进程的死亡事件。当客户端与某个服务建立了
linkToDeath关联,驱动会在服务进程退出时主动通知所有注册了死亡回调的客户端。这是 Android 系统健壮性的重要保障——例如 AMS 通过linkToDeath监听应用进程,当应用崩溃时能立即收到通知并清理资源。
注册死亡通知(linkToDeath)
- 在
IPCThreadState::requestDeathNotification()中,将BC_REQUEST_DEATH_NOTIFICATION和目标handle、cookie写入mOut,下次talkWithDriver()时送达内核。内核在binder_thread_write的BC_REQUEST_DEATH_NOTIFICATION分支中为该binder_ref分配binder_ref_death对象并挂载到ref->death。
1 | // frameworks/native/libs/binder/BpBinder.cpp |
进程死亡时的通知投递
- 当服务端进程退出时,内核调用链如下:
1 | 进程退出 |
1 | // drivers/android/binder.c |
客户端接收死亡通知
- 客户端的 Binder 线程在
binder_thread_read中取到BINDER_WORK_DEAD_BINDER工作项后,写入BR_DEAD_BINDER和cookie到用户态读缓冲,并将工作项移入proc->delivered_death暂存(等待BC_DEAD_BINDER_DONEACK)。 - 用户态
IPCThreadState::executeCommand()收到BR_DEAD_BINDER后,找到BpBinder的mObituaries列表,逐一调用DeathRecipient::binderDied(),最后发送BC_DEAD_BINDER_DONE通知内核可以清理binder_ref_death对象。
1 | sequenceDiagram |
Java Framework 层
- Binder 在 framework 层,采用 JNI 技术来调用 native(C/C++) 层的 binder 实现,从而为上层应用程序提供服务。
ServiceManager:通过getIServiceManager()方法获取的是ServiceManagerProxy对象;ServiceManager的addService、getService实际工作都交由ServiceManagerProxy的相应方法处理。ServiceManagerProxy:其成员变量mRemote指向BinderProxy对象,ServiceManagerProxy的addService、getService方法最终由mRemote完成。ServiceManagerNative:其方法asInterface()返回的是ServiceManagerProxy对象,ServiceManager通过ServiceManagerNative来获得ServiceManagerProxy。Binder:其成员变量mObject和方法execTransact()用于与 native 层交互。BinderInternal:内部有一个GcWatcher类,用于处理与 Binder 相关的垃圾回收调试。IBinder:接口中常量FLAG_ONEWAY表示调用可以非阻塞返回;它还定义了内部接口DeathRecipient,用于死亡通知。
分层关系
- Android Framework Java 层的 Binder 体系由以下几个关键类构成,它们通过 JNI 与 Native 层对接:
| Java 类 | 对应 Native 类 | 角色 |
|---|---|---|
android.os.Binder |
JavaBBinder(BBinder 子类) |
服务端本地对象基类 |
android.os.BinderProxy |
BpBinder(通过 JNI 持有) |
客户端代理对象 |
android.os.IBinder |
IBinder |
公共接口 |
android.os.Parcel |
Parcel(C++) |
数据序列化容器 |
android.os.ServiceManager |
IServiceManager(C++) |
服务注册/查询门面 |
Binder.java(服务端基类)
- Binder 是 Java 层所有本地 Binder 服务的基类,对应 Native 层的 BBinder。
系统服务(如 ActivityManagerService)通过继承其 AIDL 生成的 Stub 间接继承 Binder,并重写 onTransact() 处理来自客户端的请求。 - mObject:持有 Native 层 JavaBBinderHolder 的指针(通过 JNI 初始化)。
- JavaBBinderHolder::get() 惰性创建 JavaBBinder 对象:当 Binder Java 对象首次被传入 Parcel.writeStrongBinder() 时,才真正在内核注册 binder_node。
1 | // frameworks/base/core/java/android/os/Binder.java |
BinderProxy(客户端代理)
BinderProxy是 Java 层对BpBinder的封装,当客户端从ServiceManager.getService()获得一个远端 Binder 引用时,返回的就是BinderProxy对象。
1 | // frameworks/base/core/java/android/os/BinderProxy.java |
JNI 层
- JNI (Java Native Interface) 层是 Binder 架构中连接 Java Framework 层和 Native C++ 层的桥梁,主要负责对象转换、方法调用转发和生命周期管理。核心实现位于
frameworks/base/core/jni/android_util_Binder.cpp。
核心职责
- Java Binder → Native 转换:将 Java 层的
Binder对象转换为 Native 层的BBinder子类 - Native BpBinder → Java BinderProxy 转换:将 Native 层的代理对象包装为 Java 层的代理对象
- 方法调用桥接:实现 Java 方法到 Native 方法的双向调用
- 生命周期管理:确保 Java 对象和 Native 对象之间的引用计数同步
关键数据结构
bindernative_offsets_t (gBinderOffsets)
1 | static struct bindernative_offsets_t |
mClass: JavaBinder类的引用mExecTransact: JavaBinder.execTransact()方法的 IDmGetInterfaceDescriptor: JavaBinder.getInterfaceDescriptor()方法的 IDmObject: JavaBinder对象中存储JavaBBinderHolder指针的字段 ID
JavaBBinder 类
JavaBBinder继承自BBinder,作为 Java Binder 对象的 Native 代理onTransact()方法将 Native 调用转发到 Java 层的execTransact()方法- 使用全局引用 (
GlobalRef) 持有 Java 对象,确保在 Native 线程中也能访问
1 | class JavaBBinder : public BBinder |
JavaBBinderHolder 类
JavaBBinderHolder惰性创建和管理JavaBBinder实例- 使用弱指针 (
wp<JavaBBinder>) 避免循环引用 - 支持 VINTF 稳定性和扩展接口
1 | class JavaBBinderHolder |
BinderProxyNativeData 结构体
- 聚合
BinderProxy的所有 Native 指针字段 - 通过单个
NativeAllocationRegistry管理内存 - 包含死亡通知接收者列表
1 | struct BinderProxyNativeData { |
核心方法
- 将 Native
IBinder转换为对应的 Java 对象 - 对于
JavaBBinder返回原始 Java 对象 - 对于
BpBinder创建新的BinderProxy并管理代理数量
javaObjectForIBinder
1 | jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) |
ibinderForJavaObject
- 将 Java 对象转换为对应的 Native
IBinder - Java
Binder返回对应的JavaBBinder - Java
BinderProxy返回对应的BpBinder
1 | sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) |
核心 JNI 函数
- Java
BinderProxy.transactNative()的 JNI 实现 - 将 Java
Parcel转换为 NativeParcel并执行事务 - 处理事务错误并转换为 Java 异常
android_os_BinderProxy_transact
1 | static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, |
android_os_BinderProxy_linkToDeath
- 注册 Binder 死亡通知接收者
- 创建
JavaDeathRecipient对象并链接到 Native Binder
1 | static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj, |
Parcel 数据转换
- JNI 层还负责 Java
Parcel和 NativeParcel之间的数据转换: - Java
Parcel对象内部持有一个指向 NativeParcel的指针 - JNI 函数通过该指针直接操作 Native 对象
1 | Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { |
死亡通知机制
- JNI 层实现了 Binder 对象的死亡通知机制:
JavaDeathRecipient继承IBinder::DeathRecipientbinderDied()方法在 Binder 服务死亡时被调用- 通过 JNI 回调 Java 层的死亡接收者
JavaDeathRecipient 类
1 | class JavaDeathRecipient : public IBinder::DeathRecipient |
内存管理
- JNI 层通过
NativeAllocationRegistry确保内存安全: - 当 Java
BinderProxy对象被垃圾回收时,自动释放对应的BinderProxyNativeData - 防止 Native 层内存泄漏
1 | static const JNINativeMethod gBinderProxyMethods[] = { |
异常处理
- JNI 层需要妥善处理 Java 异常和 Native 错误:
- Native 层的
status_t错误码转换为对应的 Java 异常 - 确保错误信息正确传递到 Java 层
1 | static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, ...) { |
线程安全
- JNI 层需要处理多线程环境下的并发访问:
- 所有 JNI 函数都在适当的同步机制下执行
JavaBBinderHolder的get()方法使用互斥锁确保线程安全BinderProxyNativeData的访问需要考虑并发修改- 死亡通知机制支持在不同线程间的安全传递