Binder通信原理
# 02.Binder通信原理
# 目录介绍
- 一、引言:为什么Android选择Binder
- 二、Linux传统IPC机制对比
- 2.1 传统IPC的数据拷贝次数
- 2.2 各IPC机制的综合对比
- 2.3 Binder选择的核心原因
- 三、Binder的整体架构
- 3.1 四层架构
- 3.2 核心组件关系
- 四、Binder驱动的内核实现
- 4.1 Binder驱动的核心数据结构
- 4.2 Binder驱动的核心操作
- 4.3 binder_ioctl:通信的核心
- 五、mmap与一次拷贝原理
- 5.1 Binder的内存映射机制
- 5.2 binder_mmap的实现
- 5.3 一次拷贝的数据流
- 六、Binder通信协议与数据传输
- 6.1 Binder通信的命令协议
- 6.2 一次完整的Binder调用过程
- 6.3 Parcel:Binder的数据载体
- 七、ServiceManager的角色与实现
- 7.1 ServiceManager的定位
- 7.2 服务注册与查找流程
- 7.3 Binder对象在跨进程传输时的转换
- 八、AIDL与Proxy-Stub模式
- 8.1 AIDL的本质
- 8.2 生成代码的核心结构
- 8.3 同进程优化
- 九、Binder线程池管理
- 9.1 Binder线程池的结构
- 9.2 线程池的动态管理
- 9.3 线程池扩容策略
- 十、Binder中的引用计数与生命周期
- 10.1 Binder的引用计数机制
- 10.2 Death Notification(死亡通知)
- 十一、Binder的安全机制
- 11.1 调用者身份验证
- 11.2 SELinux对Binder的保护
- 十二、Binder在Framework层的应用
- 12.1 四大组件与Binder
- 12.2 系统API调用链路分析
- 十三、Binder的性能分析与优化
- 13.1 Binder通信的性能瓶颈
- 13.2 Binder传输大小限制
- 13.3 Binder优化实践
- 十四、面试高频问题与深度分析
- 14.1 Binder为什么是一次拷贝?共享内存不是更快吗?
- 14.2 AIDL中的in/out/inout修饰符是什么意思?
- 14.3 Binder调用是同步的还是异步的?
- 十五、IPC通信方式详解
- 15.1 Intent通信设计
- 15.2 文件共享通信设计
- 15.3 Messenger通信设计
- 15.4 AIDL跨进程通信
- 15.5 ContentProvider
- 15.6 Socket通信设计
- 15.7 IPC方式对比
- 十六、总结
# 一、引言:为什么Android选择Binder
在Linux世界中,进程间通信(IPC)有多种成熟的方案:管道、消息队列、共享内存、Socket等。然而,Android没有采用这些传统方案作为核心IPC机制,而是选择了一个来自OpenBinder项目的方案——Binder。
疑惑:Linux已经有这么多IPC机制,为什么Android还要"重新发明轮子"?
答疑:因为移动设备对IPC有特殊的需求——高性能、安全、易用。传统IPC机制要么性能不够(Socket需要两次拷贝),要么不安全(共享内存无法验证调用者身份),要么使用复杂(消息队列需要手动管理生命周期)。Binder通过内核驱动实现了一次拷贝、调用者身份验证、面向对象的接口设计,完美满足了Android的需求。
# 二、Linux传统IPC机制对比
# 2.1 传统IPC的数据拷贝次数
管道/消息队列/Socket:
发送方用户空间 → [copy_from_user] → 内核缓冲区 → [copy_to_user] → 接收方用户空间
第1次拷贝 第2次拷贝
总计:2次数据拷贝
共享内存:
发送方用户空间 ←→ 共享物理页面 ←→ 接收方用户空间
0次拷贝
总计:0次数据拷贝(但需要额外的同步机制)
Binder:
发送方用户空间 → [copy_from_user] → 内核缓冲区/接收方映射区
第1次拷贝 ↑ 接收方通过mmap直接读取
总计:1次数据拷贝
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.2 各IPC机制的综合对比
┌──────────────┬──────────┬──────────┬──────────┬──────────┐
│ 特性 │ Socket │ 共享内存 │ 管道 │ Binder │
├──────────────┼──────────┼──────────┼──────────┼──────────┤
│ 数据拷贝次数 │ 2 │ 0 │ 2 │ 1 │
│ 安全性 │ 低 │ 低 │ 低 │ 高 │
│ 身份验证 │ 无内建 │ 无内建 │ 无内建 │ UID/PID │
│ C/S架构支持 │ 支持 │ 不支持 │ 不支持 │ 天然支持 │
│ 面向对象 │ 否 │ 否 │ 否 │ 是 │
│ 使用复杂度 │ 中 │ 高 │ 低 │ 低(AIDL) │
│ 适用场景 │ 跨网络 │ 高频交互 │ 亲缘进程 │ 系统IPC │
└──────────────┴──────────┴──────────┴──────────┴──────────┘
2
3
4
5
6
7
8
9
10
11
# 2.3 Binder选择的核心原因
1. 性能:一次拷贝,介于共享内存和Socket之间的最佳平衡点
2. 安全:内核级别的UID/PID身份验证,无法伪造
3. 易用:AIDL自动生成代码,开发者只需定义接口
4. 架构:天然支持C/S模型,与Android组件化架构完美匹配
5. 引用计数:自动管理远程对象生命周期,避免内存泄漏
2
3
4
5
# 三、Binder的整体架构
# 3.1 四层架构
Binder架构(分层视图):
┌────────────────────────────────────────────────┐
│ 应用层(Application Layer) │
│ Activity/Service/ContentProvider/BroadcastReceiver│
│ 使用AIDL或Messenger进行IPC │
├────────────────────────────────────────────────┤
│ Framework层(Java Binder) │
│ IBinder / Binder / BinderProxy │
│ Parcel(数据序列化) │
│ ServiceManager(服务注册与发现) │
├────────────────────────────────────────────────┤
│ Native层(C++ Binder) │
│ BpBinder / BBinder / ProcessState / IPCThreadState│
│ libbinder.so │
├────────────────────────────────────────────────┤
│ 内核层(Binder Driver) │
│ /dev/binder │
│ binder_ioctl / binder_mmap / binder_thread_read │
└────────────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3.2 核心组件关系
Client进程 Server进程
┌─────────────────┐ ┌─────────────────┐
│ BinderProxy │ │ Binder (Stub) │
│ (Java层代理) │ │ (Java层实现) │
│ ↓ │ │ ↑ │
│ BpBinder │ │ BBinder │
│ (Native代理) │ │ (Native实现) │
│ ↓ │ │ ↑ │
│ IPCThreadState │ │ IPCThreadState │
│ ↓ │ │ ↑ │
└────────┼────────┘ └────────┼─────────┘
│ │
↓ Binder Driver ↑
┌────────────────────────────────────────────┐
│ /dev/binder │
│ binder_proc → binder_node → binder_ref │
│ binder_transaction → binder_buffer │
└────────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 四、Binder驱动的内核实现
# 4.1 Binder驱动的核心数据结构
// 每个使用Binder的进程对应一个binder_proc
struct binder_proc {
struct hlist_node proc_node; // 挂在全局进程链表中
struct rb_root threads; // 红黑树:该进程的所有Binder线程
struct rb_root nodes; // 红黑树:该进程提供的所有Binder节点
struct rb_root refs_by_desc; // 红黑树:该进程引用的远程Binder(按handle排序)
struct rb_root refs_by_node; // 红黑树:按节点指针排序
struct list_head todo; // 待处理的工作队列
struct list_head waiting_threads; // 空闲等待的线程
int pid; // 进程PID
struct vm_area_struct *vma; // mmap映射的虚拟地址区域
void *buffer; // 内核缓冲区起始地址
size_t buffer_size; // 缓冲区大小(默认1MB-8KB)
};
// 每个Binder实体对应一个binder_node
struct binder_node {
struct binder_proc *proc; // 所属进程
struct hlist_head refs; // 所有引用此节点的binder_ref列表
int internal_strong_refs; // 内部强引用计数
int local_strong_refs; // 本地强引用计数
binder_uintptr_t ptr; // 指向用户空间Binder对象
binder_uintptr_t cookie; // 附加数据
};
// 引用远程Binder的句柄
struct binder_ref {
struct binder_proc *proc; // 所属进程
struct binder_node *node; // 指向的目标Binder节点
uint32_t desc; // 句柄值(handle)
int strong; // 强引用计数
int weak; // 弱引用计数
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 4.2 Binder驱动的核心操作
// Binder驱动注册
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.open = binder_open, // 打开/dev/binder
.mmap = binder_mmap, // 内存映射
.unlocked_ioctl = binder_ioctl, // 核心通信接口
.release = binder_release, // 关闭
.poll = binder_poll, // 轮询
.flush = binder_flush, // 刷新
};
// binder_open:进程首次使用Binder时调用
static int binder_open(struct inode *nodp, struct file *filp) {
struct binder_proc *proc;
proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 分配binder_proc
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->todo);
// 将proc加入全局链表
hlist_add_head(&proc->proc_node, &binder_procs);
filp->private_data = proc;
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 4.3 binder_ioctl:通信的核心
// 所有Binder通信都通过ioctl系统调用
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
switch (cmd) {
case BINDER_WRITE_READ:
// 最核心的命令:发送和接收数据
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
break;
case BINDER_SET_MAX_THREADS:
// 设置最大线程数
proc->max_threads = *(int *)arg;
break;
case BINDER_SET_CONTEXT_MGR:
// 注册为ServiceManager(全系统只有一个)
ret = binder_ioctl_set_ctx_mgr(filp, NULL);
break;
case BINDER_VERSION:
// 获取Binder版本
break;
}
return ret;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 五、mmap与一次拷贝原理
# 5.1 Binder的内存映射机制
这是理解Binder高性能的关键。传统IPC需要两次拷贝的原因是:发送方的用户空间→内核空间(第1次),内核空间→接收方用户空间(第2次)。Binder通过mmap将接收方的用户空间和内核空间映射到同一块物理内存,从而省去第2次拷贝。
传统IPC(以Socket为例):
┌───────────────┐ ┌───────────────┐
│ Client用户空间 │ │ Server用户空间 │
│ data buffer │ │ recv buffer │
│ │ │ │ ↑ │
│ copy_from_user│ │ copy_to_user │
│ ↓ │ │ │ │
├───────────────┤ ┌──────────────────┐ ├───────────────┤
│ 内核空间 │ │ 内核缓冲区 │ │ 内核空间 │
│ │ │ kernel buffer │ │ │
└───────────────┘ └──────────────────┘ └───────────────┘
第1次拷贝 → 中转 ← 第2次拷贝
Binder的mmap优化:
┌───────────────┐ ┌───────────────┐
│ Client用户空间 │ │ Server用户空间 │
│ data buffer │ │ mmap区域 │
│ │ │ │ ↑↑↑ │
│ copy_from_user│ │ (同一物理页) │
│ ↓ │ │ ↑↑↑ │
├───────────────┤ ├───────────────┤
│ 内核空间 │ │ 内核空间 │
│ binder_buffer │ ←→ 同一物理内存 ←→ │ binder_buffer │
└───────────────┘ └───────────────┘
只需1次拷贝 物理内存被同时映射到内核空间和接收方用户空间
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 5.2 binder_mmap的实现
// binder_mmap: 为进程分配Binder缓冲区
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) {
struct binder_proc *proc = filp->private_data;
// 1. 限制映射大小(最大4MB,默认约1MB-8KB)
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
// 2. 设置为只读映射(用户空间不能直接写)
vma->vm_flags &= ~VM_WRITE;
// 3. 分配内核虚拟地址空间
area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
proc->buffer = area->addr;
proc->buffer_size = vma->vm_end - vma->vm_start;
// 4. 分配物理页面并同时映射到内核空间和用户空间
page = alloc_page(GFP_KERNEL);
// 映射到内核空间
map_kernel_range_noflush(kernel_addr, PAGE_SIZE, PAGE_KERNEL, &page);
// 映射到用户空间(同一个物理页!)
vm_insert_page(vma, user_addr, page);
// 此时:kernel_addr和user_addr指向同一物理页面
// 内核写入kernel_addr的数据,用户空间从user_addr可以直接读到
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 5.3 一次拷贝的数据流
Client发送数据到Server的完整过程:
1. Client调用 ioctl(BINDER_WRITE_READ)
→ 将数据从Client用户空间copy_from_user到内核缓冲区
→ 这是唯一的一次数据拷贝
2. 内核在Server的binder_buffer中分配空间
→ 这块内核缓冲区已经通过mmap映射到Server的用户空间
→ 数据写入内核缓冲区 = 数据写入Server的mmap区域
3. Server被唤醒,从mmap区域直接读取数据
→ 无需第二次拷贝!
2
3
4
5
6
7
8
9
10
11
12
# 六、Binder通信协议与数据传输
# 6.1 Binder通信的命令协议
Binder使用两组命令:
BC_xxx (Binder Command):用户空间 → 内核(请求)
├── BC_TRANSACTION → 发起一次RPC调用
├── BC_REPLY → 回复一次RPC调用
├── BC_ACQUIRE_RESULT → 获取引用计数操作结果
├── BC_FREE_BUFFER → 释放通信缓冲区
├── BC_INCREFS → 弱引用+1
├── BC_ACQUIRE → 强引用+1
├── BC_RELEASE → 强引用-1
├── BC_DECREFS → 弱引用-1
├── BC_ENTER_LOOPER → 线程进入Looper循环
├── BC_REGISTER_LOOPER → 注册一个新的Looper线程
└── BC_EXIT_LOOPER → 线程退出Looper循环
BR_xxx (Binder Return):内核 → 用户空间(响应)
├── BR_TRANSACTION → 收到一次RPC调用请求
├── BR_REPLY → 收到一次RPC调用回复
├── BR_ACQUIRE_RESULT → 引用计数操作结果
├── BR_DEAD_BINDER → Binder实体死亡通知
├── BR_DEAD_REPLY → 目标进程已死亡
├── BR_FAILED_REPLY → 事务失败
├── BR_SPAWN_LOOPER → 请求创建新的Looper线程
└── BR_OK → 操作成功
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6.2 一次完整的Binder调用过程
Client Binder Driver Server
│ │ │
│ BC_TRANSACTION │ │
│ (code, data, target) │ │
│──────────────────────────→│ │
│ │ 1. copy_from_user │
│ │ 2. 查找target进程 │
│ │ 3. 在target的buffer中分配空间│
│ │ 4. 拷贝数据到buffer │
│ │ 5. 将事务加入target的todo队列│
│ │ 6. 唤醒target的等待线程 │
│ │ │
│ │ BR_TRANSACTION │
│ │──────────────────────────→│
│ │ │ 处理请求
│ │ │ 执行对应方法
│ │ │
│ │ BC_REPLY │
│ │←──────────────────────────│
│ │ │
│ BR_REPLY │ │
│←──────────────────────────│ │
│ │ │
│ 获得返回结果 │ │
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6.3 Parcel:Binder的数据载体
// Parcel是Binder通信的数据序列化容器
// 内存布局:
// ┌──────────────────────────────────────┐
// │ mData(普通数据区) │
// │ ┌──────┬──────┬──────┬──────┐ │
// │ │int │String│float │byte[]│ │
// │ │4bytes│len+data│4bytes│len+data│ │
// │ └──────┴──────┴──────┴──────┘ │
// ├──────────────────────────────────────┤
// │ mObjects(Binder对象偏移量数组) │
// │ ┌──────┬──────┐ │
// │ │off1 │off2 │ ← 记录flat_binder_object的位置│
// │ └──────┴──────┘ │
// └──────────────────────────────────────┘
// 写入数据
parcel.writeInt(42);
parcel.writeString("hello");
parcel.writeStrongBinder(myBinder); // 写入Binder对象
// Binder对象在Parcel中的表示
struct flat_binder_object {
__u32 hdr_type; // BINDER_TYPE_BINDER 或 BINDER_TYPE_HANDLE
__u32 flags;
union {
binder_uintptr_t binder; // 本地Binder指针(同进程)
__u32 handle; // 远程Binder句柄(跨进程)
};
binder_uintptr_t cookie;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 七、ServiceManager的角色与实现
# 7.1 ServiceManager的定位
ServiceManager是Binder机制中的"DNS服务器",它是整个系统中第一个启动的Binder服务:
ServiceManager的特殊性:
1. 它的Binder handle固定为0(所有进程都知道如何找到它)
2. 它是通过BINDER_SET_CONTEXT_MGR ioctl注册的
3. 它维护着一个服务名→Binder引用的映射表
4. 所有系统服务都必须通过它注册和查找
类比:
ServiceManager = DNS服务器
服务名(如"activity")= 域名
Binder引用 = IP地址
2
3
4
5
6
7
8
9
10
# 7.2 服务注册与查找流程
服务注册流程:
SystemServer进程 ServiceManager进程
│ │
│ addService("activity", amsBinder) │
│───────── Binder IPC ──────────────→│
│ │ 存入映射表
│ │ "activity" → binder_ref
│←──────── 返回成功 ─────────────────│
│ │
服务查找流程:
App进程 ServiceManager进程
│ │
│ getService("activity") │
│───────── Binder IPC ──────────────→│
│ │ 查找映射表
│ │ 找到"activity"对应的binder_ref
│←──── 返回AMS的BinderProxy ────────│
│ │
│ 现在可以直接和AMS通信了 │
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 7.3 Binder对象在跨进程传输时的转换
关键概念:Binder实体在不同进程中的表现形式不同
Server进程中:Binder(实体对象)
↓ 跨进程传输
Binder驱动中:binder_node(内核数据结构)
↓ 传递给Client
Client进程中:BinderProxy(代理对象)
具体转换过程:
1. Server将Binder写入Parcel
→ flat_binder_object.type = BINDER_TYPE_BINDER
→ flat_binder_object.binder = Binder实体的指针
2. Binder驱动处理:
→ 为这个Binder创建/查找binder_node
→ 在Client进程中创建binder_ref
→ 将type改为 BINDER_TYPE_HANDLE
→ 将binder改为 handle值(binder_ref.desc)
3. Client从Parcel读取:
→ 发现是BINDER_TYPE_HANDLE
→ 创建BinderProxy对象
→ 后续调用通过BinderProxy → BpBinder → ioctl → Binder驱动 → Server
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 八、AIDL与Proxy-Stub模式
# 8.1 AIDL的本质
AIDL(Android Interface Definition Language)是一个代码生成工具,它自动生成Binder通信所需的Proxy和Stub代码:
开发者编写:
// IMyService.aidl
interface IMyService {
String getData(int id);
void setConfig(in Bundle config);
}
AIDL编译器生成:
├── IMyService.java // 接口定义
│ ├── Stub(抽象类) // 服务端基类
│ │ └── onTransact() // 接收并分发请求
│ └── Proxy(内部类) // 客户端代理
│ └── getData() // 将调用打包发送
2
3
4
5
6
7
8
9
10
11
12
13
# 8.2 生成代码的核心结构
public interface IMyService extends android.os.IInterface {
// Stub:服务端实现的基类
public static abstract class Stub extends Binder implements IMyService {
private static final String DESCRIPTOR = "com.example.IMyService";
static final int TRANSACTION_getData = IBinder.FIRST_CALL_TRANSACTION + 0;
static final int TRANSACTION_setConfig = IBinder.FIRST_CALL_TRANSACTION + 1;
// 客户端获取代理对象
public static IMyService asInterface(IBinder obj) {
if (obj == null) return null;
// 先检查是否同进程
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IMyService) {
return (IMyService) iin; // 同进程:直接返回本地对象
}
return new Proxy(obj); // 跨进程:返回代理对象
}
// 接收远程调用并分发
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) {
case TRANSACTION_getData: {
data.enforceInterface(DESCRIPTOR);
int id = data.readInt();
String result = this.getData(id); // 调用实际实现
reply.writeNoException();
reply.writeString(result);
return true;
}
case TRANSACTION_setConfig: {
data.enforceInterface(DESCRIPTOR);
Bundle config = data.readBundle();
this.setConfig(config);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
// Proxy:客户端使用的代理类
private static class Proxy implements IMyService {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public String getData(int id) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(id);
// 发起远程调用(同步阻塞)
mRemote.transact(TRANSACTION_getData, data, reply, 0);
reply.readException();
return reply.readString();
} finally {
data.recycle();
reply.recycle();
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 8.3 同进程优化
Binder的同进程优化:
跨进程调用链:
Proxy.getData() → Parcel序列化 → ioctl → 内核 → ioctl → Parcel反序列化 → Stub.onTransact()
同进程调用链(asInterface优化):
Stub.asInterface(binder) → 发现是同进程 → 直接返回Stub实例
→ 调用 stub.getData() → 直接方法调用(无IPC开销)
判断依据:
obj.queryLocalInterface(DESCRIPTOR) != null → 同进程
2
3
4
5
6
7
8
9
10
11
# 九、Binder线程池管理
# 9.1 Binder线程池的结构
每个使用Binder的进程都有一个Binder线程池:
┌────────────────────────────────────────┐
│ 进程的Binder线程池 │
│ ┌──────────┐ │
│ │ 主Binder │ ← 首次调用时创建 │
│ │ 线程 │ 通过BC_ENTER_LOOPER注册│
│ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 工作线程1 │ │ 工作线程2 │ ← 按需创建 │
│ │ │ │ │ 通过BR_SPAWN_LOOPER请求│
│ └──────────┘ └──────────┘ │
│ │
│ 默认最大线程数:15(可配置) │
│ 线程在空闲时阻塞在ioctl(BINDER_WRITE_READ)│
└────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 9.2 线程池的动态管理
// ProcessState.cpp:进程级Binder管理
ProcessState::ProcessState(const char* driver) {
mDriverFD = open(driver, O_RDWR); // 打开/dev/binder
mmap(NULL, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
// BINDER_VM_SIZE = 1MB - 8KB
mMaxThreads = DEFAULT_MAX_BINDER_THREADS; // 默认15
ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &mMaxThreads);
}
// IPCThreadState.cpp:线程级Binder管理
void IPCThreadState::joinThreadPool(bool isMain) {
// 通知驱动:此线程加入Binder线程池
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
do {
// 阻塞等待Binder请求
result = getAndExecuteCommand();
// 驱动可能发送BR_SPAWN_LOOPER:请求创建新线程
// 当所有线程都在忙时,驱动会发出此请求
} while (result != -ECONNREFUSED && result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 9.3 线程池扩容策略
Binder驱动的线程管理策略:
当收到新的事务请求时:
1. 查找目标进程的空闲Binder线程
2. 如果有空闲线程 → 唤醒并分配任务
3. 如果没有空闲线程:
a. 当前线程数 < max_threads → 发送BR_SPAWN_LOOPER,请求创建新线程
b. 当前线程数 >= max_threads → 将事务放入todo队列等待
4. 如果todo队列也满了 → 返回BR_FAILED_REPLY
注意事项:
- 主线程(BC_ENTER_LOOPER)不计入max_threads
- 每个线程同时只处理一个事务(同步模型)
- 如果Server处理慢,Client会一直阻塞在ioctl
- 因此Binder调用不应在主线程做耗时操作(否则ANR)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 十、Binder中的引用计数与生命周期
# 10.1 Binder的引用计数机制
Binder使用两级引用计数管理对象生命周期:
强引用(Strong Reference):
- 只要还有强引用,Binder实体就不会被销毁
- BC_ACQUIRE / BC_RELEASE 增减强引用
弱引用(Weak Reference):
- 弱引用不阻止实体销毁
- BC_INCREFS / BC_DECREFS 增减弱引用
- 用于缓存场景:弱引用可以在强引用归零后"复活"
示意图:
Client进程A ──strong ref──→ ┌──────────┐
Client进程B ──strong ref──→ │ Binder │ Server进程
Client进程C ──weak ref────→ │ 实体对象 │
└──────────┘
local_strong_refs = 2
local_weak_refs = 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 10.2 Death Notification(死亡通知)
// 当Server进程意外死亡时,Client可以收到通知
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
// Server进程死亡了!
// 需要重新连接或清理资源
reconnect();
}
};
// 注册死亡通知
serviceBinder.linkToDeath(deathRecipient, 0);
// 底层实现:
// 1. Client通过Binder驱动注册death notification
// 2. 驱动在Server进程的binder_proc中记录
// 3. 当Server进程退出时,内核调用binder_deferred_release()
// 4. 遍历所有注册了death notification的Client
// 5. 向Client发送BR_DEAD_BINDER消息
// 6. Client的DeathRecipient.binderDied()被调用
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 十一、Binder的安全机制
# 11.1 调用者身份验证
// Binder的安全核心:服务端可以获取调用者的真实身份
// 这个身份由内核保证,调用者无法伪造
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
// 获取调用者的UID和PID(由内核填充,不可伪造)
int callingUid = Binder.getCallingUid();
int callingPid = Binder.getCallingPid();
// 权限检查
if (checkCallingPermission("android.permission.READ_CONTACTS")
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("No permission!");
}
// 内核实现:
// binder_transaction() {
// t->sender_euid = task_euid(current); // 从进程task_struct获取
// // 这个值由内核保证,用户空间无法修改
// }
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 11.2 SELinux对Binder的保护
SELinux Binder策略示例:
# 只允许system_app域的进程调用AMS
allow system_app activity_service:service_manager find;
# 禁止untrusted_app直接调用安装服务
neverallow untrusted_app installd:binder call;
Binder驱动中的SELinux检查点:
1. binder_transaction() → 检查是否允许向目标进程发送事务
2. binder_transfer_binder() → 检查是否允许传递Binder对象
3. binder_transfer_file() → 检查是否允许传递文件描述符
2
3
4
5
6
7
8
9
10
11
12
# 十二、Binder在Framework层的应用
# 12.1 四大组件与Binder
Android四大组件的通信全部依赖Binder:
Activity:
App → Binder → AMS.startActivity() // 启动Activity
AMS → Binder → App.scheduleLaunchActivity() // 回调创建Activity
Service:
App → Binder → AMS.startService() // 启动Service
App → Binder → AMS.bindService() // 绑定Service
Service → Binder → App (onServiceConnected) // 回调返回IBinder
BroadcastReceiver:
App → Binder → AMS.broadcastIntent() // 发送广播
AMS → Binder → App.scheduleReceiver() // 分发广播
ContentProvider:
App → Binder → AMS.getContentProvider() // 获取Provider
App → Binder → Provider.query/insert/... // 数据操作
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 12.2 系统API调用链路分析
以getSystemService为例,展示完整的Binder调用链:
// 应用代码
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
// 调用链:
Context.getSystemService("window")
→ SystemServiceRegistry.getSystemService()
→ new WindowManagerImpl(context)
→ WindowManagerGlobal.getWindowManagerService()
→ ServiceManager.getService("window")
→ BinderProxy // WMS的代理对象
→ IWindowManager.Stub.asInterface(binder)
→ IWindowManager.Proxy // WMS的AIDL代理
// 后续调用:
wm.addView(view, params)
→ WindowManagerGlobal.addView()
→ ViewRootImpl.setView()
→ mWindowSession.addToDisplayAsUser(...)
→ Proxy.addToDisplayAsUser(...) // Binder IPC
→ WMS端 Stub.onTransact()
→ Session.addToDisplayAsUser()
→ WMS.addWindow() // 真正的窗口添加逻辑
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 十三、Binder的性能分析与优化
# 13.1 Binder通信的性能瓶颈
Binder调用的耗时分解:
Client端:
├── Parcel序列化 ~5-50μs(取决于数据量)
├── ioctl系统调用 ~10-20μs
├── 内核调度等待 ~50-500μs(取决于Server负载)
└── Parcel反序列化 ~5-50μs
Server端:
├── 线程唤醒 ~10-50μs
├── Parcel反序列化 ~5-50μs
├── 业务逻辑执行 变化很大
├── Parcel序列化 ~5-50μs
└── ioctl返回 ~10-20μs
典型的空调用(无业务逻辑)延迟:~100-200μs
有数据的调用延迟:~200μs - 数毫秒
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 13.2 Binder传输大小限制
Binder缓冲区限制:
- 普通进程:1MB - 8KB ≈ 1016KB
- ServiceManager:128KB
超出限制会抛出 TransactionTooLargeException
大数据传输方案:
┌──────────────────┬──────────────────────────────┐
│ 数据量 │ 推荐方案 │
├──────────────────┼──────────────────────────────┤
│ < 100KB │ 直接通过Binder传输 │
│ 100KB ~ 1MB │ 使用Bundle传输,注意分块 │
│ > 1MB │ 使用ContentProvider + Cursor │
│ │ 或使用共享内存(Ashmem) │
│ │ 或使用文件描述符传递 │
│ 超大文件 │ 传递文件路径或ParcelFileDescriptor │
└──────────────────┴──────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 13.3 Binder优化实践
1. 减少IPC调用次数(批量操作优于多次调用):
// 差:多次IPC
for (Item item : items) {
service.addItem(item); // 每次都是一次IPC
}
// 好:一次IPC
service.addItems(items); // 批量传输
2. 使用oneway修饰符(异步调用,不等待返回):
// AIDL中声明
oneway interface ICallback {
void onResult(String result); // 异步回调,不阻塞调用者
}
3. 避免在主线程进行Binder调用:
// 差:主线程阻塞
String data = service.getData();
// 好:子线程调用
executor.execute(() -> {
String data = service.getData();
handler.post(() -> updateUI(data));
});
4. 合理设置Binder线程池大小:
// 默认15个线程,如果Server并发请求多,可以适当增大
BinderInternal.setMaxThreads(20);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 十四、面试高频问题与深度分析
# 14.1 Binder为什么是一次拷贝?共享内存不是更快吗?
一次拷贝 vs 零拷贝(共享内存):
Binder(一次拷贝):
优点:安全(数据通过内核中转,可以做权限检查)
缺点:比共享内存多一次拷贝
共享内存(零拷贝):
优点:最快(无任何拷贝)
缺点:
- 无法验证对方身份(任何映射到共享内存的进程都能读写)
- 需要额外的同步机制(信号量/互斥锁)
- 无法做权限控制(内核不参与传输过程)
Android的选择:安全 > 极致性能
对于系统级IPC,安全比多省一次拷贝更重要
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 14.2 AIDL中的in/out/inout修饰符是什么意思?
// in:数据从Client传到Server(默认)
void sendData(in Bundle data);
// Client: 序列化data → Server: 反序列化得到data
// Server修改data不会影响Client的data
// out:数据从Server传回Client
void getData(out Bundle data);
// Client: 发送空Bundle → Server: 填充data → Client: 收到填充后的data
// inout:双向传输
void processData(inout Bundle data);
// Client: 序列化data → Server: 修改data → Client: 收到修改后的data
// 注意:inout需要两次拷贝,性能最差
2
3
4
5
6
7
8
9
10
11
12
13
# 14.3 Binder调用是同步的还是异步的?
默认:同步阻塞调用
Client调用 → 阻塞等待 → Server处理 → 返回结果 → Client继续
异步(oneway):
声明:oneway interface ICallback { void onResult(String data); }
Client调用 → 立即返回 → Server在Binder线程中处理
注意:oneway调用没有返回值,且不保证顺序
特殊情况(同进程):
如果Client和Server在同一进程
→ 不经过Binder驱动
→ 直接方法调用(最快)
→ asInterface()中的queryLocalInterface判断
2
3
4
5
6
7
8
9
10
11
12
13
# 十五、IPC通信方式详解
除了Binder之外,Android还提供了多种IPC通信方式,开发者可以根据具体需求选择适当的方案。
# 15.1 Intent通信设计
Activity、Service、Receiver都支持在Intent中传递Bundle数据,而Bundle实现了Parcelable接口,可以在不同的进程间进行传输。
使用场景举例:App通过Intent调用打电话;App获取手机存储中图片资源;App监听手机屏幕亮灭屏事件;App分享文件到QQ等,都属于跨进程通信。
# 15.2 文件共享通信设计
Android系统基于Linux,并发读取文件没有限制,甚至允许两个线程同时对一个文件进行读写操作。可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意并不是同一个对象,只是内容相同)。
注意:SharedPreferences不适合用于IPC,系统对它的读写有一定的缓存策略,在高并发读写场景下有很大几率丢失数据。
# 15.3 Messenger通信设计
Messenger是一种轻量级的IPC方案,底层实现是AIDL,一次只处理一个请求,在服务端不需要考虑线程同步的问题。
Messenger通信流程:
服务端:
Service → Handler → Messenger → onBind返回Binder
客户端:
bindService → 获取IBinder → 创建Messenger → 发送Message
↕ (双向通信时)
客户端创建Handler+Messenger → 通过Message.replyTo传递给服务端
2
3
4
5
6
7
8
9
关键点:
- 客户端通过bindService的onServiceConnected获取服务端Messenger
- 服务端通过Message.replyTo获取客户端Messenger
- Messenger中的Handler以串行方式处理队列中的消息,不存在并发执行
# 15.4 AIDL跨进程通信
AIDL可以解决并发和跨进程调用方法的问题,Messenger本质上也是AIDL,只不过系统做了封装方便上层调用。
AIDL通信步骤:
服务端:
- 新建定义AIDL文件,声明服务需要向客户端提供的接口
- 在Service子类中实现AIDL中定义的接口方法
- 在AndroidManifest.xml中注册服务并声明为远程服务
客户端:
- 拷贝服务端的AIDL文件到目录下
- 使用Stub.asInterface接口获取服务器的Binder,调用服务提供的接口方法
- 通过Intent指定服务端的服务名称和所在包,绑定远程Service
注意:客户端调用远程服务的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,如果服务端方法执行耗时,会导致客户端ANR。
# 15.5 ContentProvider
ContentProvider是Android中用于数据共享的IPC机制。继承ContentProvider类实现6个抽象方法,这六个方法均运行在ContentProvider进程中,除onCreate运行在主线程里,其他五个方法由外界回调运行在Binder线程池中。
底层数据可以是SQLite数据库、文件,也可以是内存中的数据。
ContentProvider的跨进程通信原理:
App进程A system_server App进程B
│ │ │(ContentProvider所在进程)
│ getContentResolver() │ │
│ .query(uri, ...) │ │
│ → ContentResolver │ │
│ .acquireProvider(uri) │ │
│ ────Binder──→ │ AMS.getContentProvider() │
│ │ → 查找已发布的Provider │
│ │ → 如果Provider进程未启动 │
│ │ → 先启动进程 │
│ │ ────返回IContentProvider│
│ ←────Binder── │ Binder代理对象 │
│ │ │
│ 后续直接通过IContentProvider的Binder代理通信 │
│ ───────────── Binder IPC ──────────────────→ │
│ │ query() 在Binder线程池执行
│ ←──────────── Cursor(CursorWindow) ──────────── │
关键设计:
1. 首次访问通过AMS获取Provider代理,后续直接P2P通信
2. CursorWindow使用匿名共享内存(ashmem)传输大量数据
→ 突破Binder 1MB限制
→ CursorWindow默认大小2MB
3. ContentProvider支持批量操作applyBatch(),减少IPC次数
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 15.6 Socket通信设计
Socket起源于Unix,可以通过本地回环地址(127.0.0.1)进行进程间通信。常用的Socket类型有两种:
- 流式Socket(SOCK_STREAM):面向连接的,针对TCP服务应用
- 数据报式Socket(SOCK_DGRAM):无连接的,对应UDP服务应用
一个Socket拥有两个缓冲区(一读一写),一次通信需要经历2次数据复制。
Socket在Android系统中的应用:
1. Zygote通信
AMS通过LocalSocket与Zygote通信
→ 为什么不用Binder?
→ 因为Binder需要先初始化ServiceManager
→ 而Zygote启动时ServiceManager可能还未就绪
→ Socket是更原始、更可靠的通信方式
2. ADB通信
adb server ←TCP Socket→ adbd守护进程
→ 支持远程调试(adb connect ip:5555)
3. LocalSocket(Unix Domain Socket)
不经过网络协议栈,性能优于TCP Socket
使用文件路径作为地址(如/dev/socket/zygote)
→ 比TCP快约2-3倍(无网络层开销)
4. Socket vs Binder对比
├── Socket:全双工,支持流式传输,2次拷贝
├── Binder:半双工(需等待reply),1次拷贝,支持身份验证
└── 选择原则:系统底层用Socket,应用层用Binder
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 15.7 IPC方式对比
| IPC方式 | 数据拷贝次数 | 安全性 | 特点 |
|---|---|---|---|
| 共享内存 | 0次 | 低 | 性能最好,但控制复杂 |
| Binder | 1次 | 高 | Android推荐,支持身份验证 |
| 管道 | 2次 | 中 | 固定读写端,可能阻塞 |
| 消息队列 | 2次 | 中 | 克服了管道的缺点 |
| Socket | 2次 | 中 | 支持C/S模式,适合网络通信 |
# 十六、总结
Binder是Android系统最核心的基础设施,理解Binder就是理解Android架构的钥匙:
Binder的核心知识图谱:
基础原理
├── mmap + 一次拷贝 → 高性能
├── 内核UID验证 → 高安全
├── 引用计数 → 自动生命周期管理
└── C/S + Proxy-Stub → 面向对象的IPC
系统应用
├── ServiceManager → 服务注册中心
├── AMS/PMS/WMS → 通过Binder提供服务
├── 四大组件 → 通过Binder与AMS交互
└── Intent/ContentProvider → Binder之上的高级抽象
开发实践
├── AIDL → 自动生成Binder代码
├── Messenger → 基于Handler的简单IPC
├── ContentProvider → 数据共享
└── 性能优化 → 减少调用次数、异步化、合理设置线程池
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
从Linux驱动层的binder_ioctl,到Framework层的IBinder接口,再到应用层的AIDL,Binder贯穿了Android系统的每一层。掌握Binder的原理,是成为Android高级开发者的必经之路。