目录
  1. 1. 一、Binder 通信模型全景
    1. 1.1. 1.1 四角色模型
    2. 1.2. 1.2 ServiceManager 的注册与发现
    3. 1.3. 1.3 Binder 对象在进程间的传递
    4. 1.4. 1.4 内存划分
    5. 1.5. 1.5 传统 IPC 传输数据
    6. 1.6. 1.6 Binder 传输数据
  2. 2. 二、Linux 进程间通信机制对比
    1. 2.1. 2.1 三种 IPC 方式的数据拷贝次数
  3. 3. 三、面向对象的 Binder IPC
  4. 4. 四、Binder 驱动深度分析
    1. 4.1. 4.1 驱动注册:binder_init
    2. 4.2. 4.2 打开设备:binder_open
    3. 4.3. 4.3 内存映射:binder_mmap
    4. 4.4. 4.4 核心 ioctl:binder_ioctl
    5. 4.5. 4.5 binder_thread_write —— 处理用户空间命令
    6. 4.6. 4.6 binder_transaction —— 核心事务处理
  5. 5. 五、Binder 用户空间实现
    1. 5.1. 5.1 ProcessState 与 IPCThreadState
    2. 5.2. 5.2 Binder 线程池
  6. 6. 六、Java 层 Binder 实现
    1. 6.1. 6.1 BinderProxy 和 Binder
    2. 6.2. 6.2 AIDL 的代码生成
    3. 6.3. 6.3 oneway vs 同步调用
  7. 7. 七、Binder 的安全机制
    1. 7.1. 7.1 UID/PID 验证
    2. 7.2. 7.2 SELinux 对 Binder 的约束
  8. 8. 八、Binder 性能优化与限制
    1. 8.1. 8.1 事务大小限制
    2. 8.2. 8.2 Binder 线程耗尽
  9. 9. 九、剑指大厂 — 核心面试题
【吃透源码系列】之Binder

我们知道Android系统启动之后,要想启动一个应用程序则需要保证该应用程序的进程先被启动,如果该进程不存在就会请求 Zygote 进程启动需要的应用程序进程。这样在 Zygote 的 Java 框架层(Framework源码层)中会创建一个 Server 端的 Socket,这个 Socket 用来等待 AMS 请求 Zygote 来创建新的应用程序进程(Zygote 进程通过 fork 自身创建应用程序进程),这样应用程序进程就会获得 Zygote 进程在启动时创建的虚拟机实例。与此同时,也创建出了 Binder 线程池消息循环,这样运行在应用进程中的应用程序就可以很方便的使用 Binder 进行进程间通信以及处理消息了。

一、Binder 通信模型全景

1.1 四角色模型

Binder 框架定义了四个角色:ServerClientServiceManagerBinder驱动,这其中前三个位于 用户空间,驱动位于 内核空间。这四个角色的关系和互联网类似:Server 是服务器,Client 是客户终端,SMgr 是域名服务器(DNS),驱动是路由器。

┌─────────────┐                    ┌─────────────┐
│ Client │ │ Server │
│ (App进程) │ │ (服务进程) │
│ │ │ │
│ BpBinder │ │ BBinder │
│ └─BpRefBase│ │ └─IBinder │
│ └─handle │ │ └─mObject│
└──────┬──────┘ └──────┬──────┘
│ │
│ ioctl(BINDER_WRITE_READ) │ ioctl(BINDER_WRITE_READ)
│ │
┌──────┴──────────────────────────────────┴──────┐
│ Binder Driver (内核) │
│ │
│ binder_proc (Client) binder_proc (Server) │
│ ├── todo 队列 ├── todo 队列 │
│ ├── threads 链表 ├── threads 链表 │
│ └── nodes 引用表 └── nodes 实体表 │
│ │
│ binder_node ←── 引用(SMgr负责查找)──→ binder_ref│
└────────────────────────────────────────────────┘

┌──────┴──────┐
│ServiceManager│ (servicemanager进程, handle=0)
│ binder_context │
│ ├── service列表 │
│ │ ├── "activity" → handle=X │
│ │ ├── "package" → handle=Y │
│ │ └── ... │
└──────────────┘

1.2 ServiceManager 的注册与发现

ServiceManager 是 Binder 体系中的”命名服务”。它是一个特殊的 Binder 服务,固定句柄为 handle=0。

// frameworks/native/cmds/servicemanager/ServiceManager.cpp
int main() {
// 1. 打开 Binder 驱动
driver = open("/dev/binder", O_RDWR | O_CLOEXEC);

// 2. 将自己注册为 context manager (handle=0)
ioctl(driver, BINDER_SET_CONTEXT_MGR, 0);

// 3. 进入 Binder 循环
binder_loop(driver, svcmgr_handler);
}

// 服务的注册
int svcmgr_handler(struct binder_state *bs,
struct binder_txn *txn,
struct binder_io *msg,
struct binder_io *reply) {
switch (txn->code) {
case SVC_MGR_ADD_SERVICE:
// 将服务名和 handle 绑定
s = strdup(svcmgr_lookup(txn, ...));
svc->handle = bio_get_ref(msg); // 获取服务的 handle
svclist_push(svc);
break;

case SVC_MGR_GET_SERVICE:
// 根据服务名查找 handle
svc = svclist_find(svc_name);
bio_put_ref(reply, svc->handle);
break;

case SVC_MGR_CHECK_SERVICE:
// 检查服务是否存在
break;
}
}

注册流程

Server 进程                          ServiceManager 进程
│ │
│ 1. new BBinder() │
│ 2. defaultServiceManager() │
│ -> 获取 SMgr 的 BpBinder(0) │
│ │
│ 3. addService("myservice", bbinder) │
│ -> BpServiceManager.addService() │
│ -> transact(ADD_SERVICE) │
│ -> Parcel 携带: │
│ - 服务名 "myservice" │
│ - bbinder 的 flat_binder│
│ │
├─────────────────────────────────────►│
│ │ 4. 收到 flat_binder
│ │ 5. 驱动创建 binder_ref
│ │ (SMgr->Server的引用)
│ │ 6. 存入 svclist:
│ │ "myservice" -> handle=X

发现流程

Client 进程                           ServiceManager 进程
│ │
│ 1. getService("myservice") │
│ -> transact(GET_SERVICE) │
├─────────────────────────────────────►│
│ │ 2. svclist_find("myservice")
│ │ 3. 返回 handle=X
│◄─────────────────────────────────────┤
│ │
│ 4. 驱动为 Client 创建 binder_ref │
│ -> BpBinder(handle=X) │
│ 5. 现在 Client 可以向 Server 发请求 │

1.3 Binder 对象在进程间的传递

这是 Binder 最精妙的设计之一——Binder 对象可以在进程之间传递,驱动自动管理其引用:

进程A持有 BBinder(实体) 

│ A 通过 Binder 向 B 传递此 BBinder

驱动检测到 BINDER_TYPE_BINDER (本地实体 -> 对端)

│ 在进程B中创建一个 binder_ref,分配一个新的 handle

进程B获得 BpBinder(handle=K)

│ 如果B再传递给C

驱动检测到 BINDER_TYPE_HANDLE (远程引用 -> 对端)

│ 在进程C中创建另一个 binder_ref,指向同一个 binder_node

进程C获得 BpBinder(handle=M)

这样,无论 Binder 对象经过多少层传递,驱动都能正确路由——所有引用最终指向同一个 binder_node

1.4 内存划分

内存划分.png

1.5 传统 IPC 传输数据

传统 IPC 传输数据.png

1.6 Binder 传输数据

Binder 传输数据.png

二、Linux 进程间通信机制对比

Binder 是 Android 系统进程间通信(IPC)方式之一,这里为了突出 Android Binder IPC 通信机制的优越性,先分析对比一波 共享内存Socket 和本文介绍的 Binder 的数据拷贝。

2.1 三种 IPC 方式的数据拷贝次数

图片1.png

共享内存:虽然无需拷贝,但控制复杂,难以复用。基于此方案的通信,如果有恶意程序或某个app运行崩溃,会导致关联的应用甚至系统崩溃,这是致命的。

Socket/管道/消息队列:基于 C/S 架构的通信。socket 作为通用接口,传输效率低效,开销巨大,主要用在跨网络的进程间通信和本机上进程间的低速通信。如果采用 socket 在底层架设一套协议,会加重系统设计的复杂性,且难以保证通信效率。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少两次拷贝。

Binder:基于 C/S 通信模式,传输过程只需一次拷贝,为发送方添加 UID/PID 身份,既支持实名 Binder,也支持匿名 Binder,安全性高。

三、面向对象的 Binder IPC

Binder 使用 Client-Server 通信方式:一个进程作为 Server 提供诸如视频/音频解码、视频捕获、地址本查询、网络连接等服务;多个进程作为 Client 向 Server 发起服务请求,获得所需要的服务。

要想实现 Client-Server 通信必须实现以下两点:

  • Server 必须有确定的访问接入点或者说地址来接受 Client 的请求,并且 Client 可以通过某种途径获知 Server 的地址;
  • 制定 Command-Reply 协议来传输数据。

对 Binder 而言,Binder 可以看成 Server 提供的实现某个特定服务的访问接入点,Client 通过这个”地址”向 Server 发送请求来使用该服务。对 Client 而言,Binder 可以看成是通向 Server 的管道入口,要想和某个 Server 通信首先必须建立这个管道并获得管道入口。

Binder 使用了面向对象思想来描述作为访问接入点的 Binder 和在 Client 中的入口:

  • Binder 是一个实体位于 Server 中的对象,该对象提供了一套方法用以实现对服务的请求,就象类的成员函数。
  • 遍布于 Client 中的入口可以看成指向这个 Binder 对象的”指针”,一旦获得了这个”指针”就可以调用该对象的方法访问 Server。

四、Binder 驱动深度分析

4.1 驱动注册:binder_init

Linux 一切皆文件。Binder 驱动的初始化从 binder_init 开始:

// drivers/android/binder.c (Android 10+, 旧版在 drivers/staging/android/binder.c)
static int __init binder_init(void)
{
int ret;

// 1. 分配内存(binder_transaction 等对象的 slab 缓存)
binder_alloc_init();

// 2. 注册 misc 设备 "/dev/binder"
ret = misc_register(&binder_miscdev);

// 3. 放入设备链表 binder_devices
// 支持多个 Binder 设备(如 /dev/binder, /dev/hwbinder, /dev/vndbinder)
list_add(&binder_devices, &binder_miscdev.list);

return ret;
}

static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops // 文件操作表
};

static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl, // 主要的入口点
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};

4.2 打开设备:binder_open

当用户空间进程调用 open("/dev/binder", O_RDWR) 时:

static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;

// 1. 创建 binder_proc 进程对象
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
proc->tsk = current->group_leader;

// 2. 初始化 todo 队列(待处理的工作队列)
INIT_LIST_HEAD(&proc->todo);

// 3. 初始化等待队列
init_waitqueue_head(&proc->wait);

// 4. 初始化线程红黑树
proc->threads = RB_ROOT;

// 5. 初始化 binder_node 红黑树和 binder_ref 红黑树
proc->nodes = RB_ROOT;
proc->refs_by_desc = RB_ROOT; // 按 handle (描述符) 索引
proc->refs_by_node = RB_ROOT; // 按 binder_node 索引

// 6. 串联进程链表
// binder_procs -> binder_proc -> binder_proc -> ...
hlist_add_head(&proc->proc_node, &binder_procs);

// 7. 将 proc 赋值给 filp->private_data
// 后续所有通过此 fd 的操作都可以取回 proc
filp->private_data = proc;

return 0;
}

关键数据结构 binder_proc

struct binder_proc {
struct hlist_node proc_node; // 全局进程链表节点
struct rb_root threads; // Binder 线程红黑树
struct rb_root nodes; // Binder 实体(BBinder)红黑树
struct rb_root refs_by_desc; // Binder 引用(按 handle 排序)
struct rb_root refs_by_node; // Binder 引用(按 binder_node 排序)
struct list_head todo; // 待处理的事务队列
struct binder_alloc alloc; // Binder 内存分配器(替换旧版的 buffer)
struct task_struct *tsk; // 对应的内核 task_struct
// ...
};

4.3 内存映射:binder_mmap

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct binder_proc *proc = filp->private_data;

// 1. 分配内存:使用 binder_alloc(Android 10+ 替换了旧版的 vm_struct)
// 旧版:proc->buffer = area->addr (内核虚拟地址)
// 新版:使用 binder_alloc 管理物理页面
ret = binder_alloc_mmap_handler(&proc->alloc, vma);

// 2. 映射页表到用户空间,使得用户空间可以直接访问这块内存
// 3. 用户空间通过 mmap 返回的指针访问
// 内核空间通过 proc->alloc 访问
// 两者指向同一块物理内存 → 一次拷贝的基础

return ret;
}

关键:用户空间地址 + proc->alloc 偏移 = 内核空间地址,实现了”一次拷贝”。

4.4 核心 ioctl:binder_ioctl

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;

// 1. 获取或创建当前线程的 binder_thread 对象
thread = binder_get_thread(proc);

switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;

// 2. 从用户空间拷贝命令参数
copy_from_user(&bwr, ubuf, sizeof(bwr));

// 3. 处理写操作(向 Binder 驱动发送命令)
if (bwr.write_size > 0) {
binder_thread_write(proc, thread,
bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
}

// 4. 处理读操作(从 Binder 驱动读取回复)
if (bwr.read_size > 0) {
binder_thread_read(proc, thread,
bwr.read_buffer, bwr.read_size, &bwr.read_consumed, ...);
}

// 5. 将结果拷贝回用户空间
copy_to_user(ubuf, &bwr, sizeof(bwr));
break;
}

case BINDER_SET_MAX_THREADS:
// 设置最大线程数(默认 15)
proc->max_threads = (u32)arg;
break;

case BINDER_SET_CONTEXT_MGR:
// 将当前进程注册为 ServiceManager (Context Manager)
binder_context_mgr_node = binder_new_node(proc, ...);
binder_context_mgr_uid = current->cred->euid;
break;

case BINDER_THREAD_EXIT:
// 线程退出
binder_free_thread(proc, thread);
break;

case BINDER_VERSION:
// 返回 Binder 协议版本
put_user(BINDER_CURRENT_PROTOCOL_VERSION, (int __user *)arg);
break;
}

return ret;
}

4.5 binder_thread_write —— 处理用户空间命令

static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
void __user *buffer = (void __user *)binder_buffer;
void __user *ptr = buffer + *consumed;

while (ptr < buffer + size) {
// 从用户空间读取命令
get_user(cmd, (uint32_t __user *)ptr);
ptr += sizeof(uint32_t);

switch (cmd) {
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;

// 1. 从用户空间拷贝事务数据
copy_from_user(&tr, ptr, sizeof(tr));

// 2. 执行事务
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY);
break;
}

case BC_INCREFS:
case BC_ACQUIRE:
// 增加引用计数(弱引用/强引用)
binder_inc_refs(proc, thread, ...);
break;

case BC_DECREFS:
case BC_RELEASE:
// 减少引用计数
binder_dec_refs(proc, thread, ...);
break;

case BC_REGISTER_LOOPER:
// 注册一个 Binder 线程
thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
break;

case BC_ENTER_LOOPER:
// 主线程进入 Binder 循环
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;

case BC_REQUEST_DEATH_NOTIFICATION:
// 注册死亡通知
binder_request_death_notification(proc, thread, ...);
break;

case BC_CLEAR_DEATH_NOTIFICATION:
// 清除死亡通知
binder_clear_death_notification(proc, thread, ...);
break;
}
}
return 0;
}

4.6 binder_transaction —— 核心事务处理

static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr,
int reply)
{
struct binder_transaction *t;
struct binder_proc *target_proc;

if (reply) {
// 回复场景:目标进程已在原事务中确定
// ...
} else {
// 请求场景:根据 handle 找到目标进程
if (tr->target.handle) {
// 非 0 handle:通过 binder_ref 查找目标
struct binder_ref *ref;
ref = binder_get_ref_for_node(proc, tr->target.handle);
target_proc = ref->proc;
target_node = ref->node;
} else {
// handle=0:目标是 ServiceManager
target_proc = binder_context_mgr_node->proc;
target_node = binder_context_mgr_node;
}
}

// 1. 分配 binder_transaction 结构体
t = kzalloc(sizeof(*t), GFP_KERNEL);

// 2. 记录调用者身份(安全机制)
t->from_euid = task_euid(proc->tsk);
t->from_pid = proc->pid;

// 3. 从用户空间拷贝数据到内核缓冲区(一次拷贝)
// 目标进程可以通过 mmap 直接访问内核缓冲区
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, ...);
copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size);

// 4. 处理 Binder 对象(flat_binder vs handle)
// 如果是 BINDER_TYPE_BINDER:在目标进程中创建 binder_ref
// 如果是 BINDER_TYPE_HANDLE:查找已存在的引用
binder_translate_binder(t, ...);

// 5. 将事务挂入目标进程的 todo 队列
binder_enqueue_work(target_proc, t->work, &target_proc->todo);

// 6. 唤醒目标进程的等待线程
wake_up_interruptible(&target_proc->wait);
}

五、Binder 用户空间实现

5.1 ProcessState 与 IPCThreadState

// frameworks/native/libs/binder/ProcessState.cpp
// ProcessState 是进程单例,管理 /dev/binder 连接和线程池
sp<ProcessState> ProcessState::self() {
if (gProcess != NULL) return gProcess;
gProcess = new ProcessState("/dev/binder");
return gProcess;
}

ProcessState::ProcessState(const char* driver)
: mDriverFD(open_driver(driver)) // 打开 /dev/binder
, mVMStart(MAP_FAILED)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS) // 默认 15
{
// mmap 映射 Binder 缓冲区:1MB - 8KB
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ,
MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
}
// frameworks/native/libs/binder/IPCThreadState.cpp
// IPCThreadState 是线程单例,管理 Binder 通信(线程级别的 send/receive)
IPCThreadState* IPCThreadState::self() {
if (gHaveTLS.load()) {
return (IPCThreadState*)pthread_getspecific(gTLS);
}
gHaveTLS.store(true);
IPCThreadState* st = new IPCThreadState;
pthread_setspecific(gTLS, st);
return st;
}

// 发送事务的核心方法
status_t IPCThreadState::transact(int32_t handle,
uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags)
{
// 1. 写入事务命令(BC_TRANSACTION)
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data);

// 2. 如果是同步调用(非 TF_ONE_WAY),等待响应
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = waitForResponse(reply);
}
}

return err;
}

// 等待响应(同步调用)
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;

while (1) {
// 1. 与 Binder 驱动交互:发送数据,接收命令
if ((err = talkWithDriver()) < NO_ERROR) break;

// 2. 处理驱动返回的命令
cmd = (uint32_t)mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
// 对于 oneway 调用,直接完成
if (!reply && !acquireResult) goto finish;
break;

case BR_REPLY:
// 收到回复
err = mIn.read(reply, ...);
goto finish;

case BR_DEAD_REPLY:
// 目标进程已死亡
err = DEAD_OBJECT;
goto finish;

case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;

default:
// 将其他命令放入 Parcel 供上层处理
err = executeCommand(cmd);
break;
}
}
finish:
return err;
}

// 与驱动通信
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
binder_write_read bwr;

// 1. 设置输出数据(要发送的命令)
bwr.write_buffer = (uintptr_t)mOut.data();
bwr.write_size = mOut.dataSize();

// 2. 设置输入缓冲区(接收驱动回复的位置)
bwr.read_buffer = (uintptr_t)mIn.data();
bwr.read_size = mIn.dataCapacity();

// 3. ioctl 调用
status_t err = ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr);

// 4. 处理结果
mOut.setDataSize(0); // 清空输出缓冲区
return err;
}

5.2 Binder 线程池

// 每个 Server 进程启动后需要注册 Binder 线程
void IPCThreadState::joinThreadPool(bool isMain)
{
// 1. 通知驱动:主线程进入 loop(BC_ENTER_LOOPER)
// 或:注册一个新线程(BC_REGISTER_LOOPER)
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

// 2. 进入无限循环
do {
// 2.1 处理已接收的命令
result = getAndExecuteCommand();

// 2.2 如果没有待处理命令,等待新命令
if (result == TIMED_OUT && !isMain) {
break; // 非主线程超时退出
}
} while (result != -ECONNREFUSED && result != -EBADF);

// 3. 线程退出,通知驱动(BC_EXIT_LOOPER)
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}

线程池管理的关键规则:

  • 主线程(调用 joinThreadPool(true)):进入后一直等待,不退出,收到 BC_ENTER_LOOPER 命令。
  • 工作线程(调用 joinThreadPool(false)):收到 BC_REGISTER_LOOPER 命令,空闲超时后自动退出。
  • 最大线程数:默认 15,由 BINDER_SET_MAX_THREADS ioctl 设置。
  • 驱动会根据队列中的未处理事务数量动态唤醒线程。所有线程忙时,Client 的请求在 todo 队列中等待。

六、Java 层 Binder 实现

6.1 BinderProxy 和 Binder

// frameworks/base/core/java/android/os/BinderProxy.java
public final class BinderProxy implements IBinder {
private final long mNativeData; // 指向 native BpBinder

public boolean transact(int code, Parcel data, Parcel reply, int flags) {
// 调用 native transact -> BpBinder::transact
return transactNative(code, data, reply, flags);
}

// 获取调用者 UID/PID(通过 native 方法从驱动获取)
public static final native int getCallingUid();
public static final native int getCallingPid();
}

// frameworks/base/core/java/android/os/Binder.java
public class Binder implements IBinder {
private final long mObject; // 指向 native BBinder

// 服务端重写此方法处理请求
protected boolean onTransact(int code, @NonNull Parcel data,
@Nullable Parcel reply, int flags) {
// ...
}

// 直接获取当前线程的调用者 UID(驱动保证,无法伪造)
public static final int getCallingUid() {
return BinderProxy.getCallingUid();
}

public static final int getCallingPid() {
return BinderProxy.getCallingPid();
}
}

6.2 AIDL 的代码生成

AIDL(Android Interface Definition Language)是我们日常使用 Binder 的最常用方式。编译器(aidl tool)会根据 .aidl 文件生成 Java 代码:

// 示例 AIDL:IMyService.aidl
interface IMyService {
String getMessage(int id);
}

// 生成的 Java Proxy (Client 端):
public static class Proxy implements IMyService {
private IBinder mRemote;

@Override
public String getMessage(int id) {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
String _result;

try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(id);

// 通过 Binder 驱动发送请求
mRemote.transact(Stub.TRANSACTION_getMessage, _data, _reply, 0);

_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}

return _result;
}
}

// 生成的 Stub (Server 端):
public static abstract class Stub extends Binder implements IMyService {
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
switch (code) {
case TRANSACTION_getMessage: {
data.enforceInterface(DESCRIPTOR);
int _arg0 = data.readInt();
String _result = this.getMessage(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}

6.3 oneway vs 同步调用

// 同步调用(默认)
// Client 阻塞等待 Server 返回结果
mRemote.transact(code, data, reply, 0);

// oneway 调用 (异步)
// Client 发送请求后立即返回,不等待结果
mRemote.transact(code, data, null, IBinder.FLAG_ONEWAY);

// 在 AIDL 中声明 oneway
// oneway interface IMyService {
// void fireAndForget(int value); // 不能有返回值
// }

oneway 的内部机制:

  1. Client 端:IPCThreadState::transactflags & TF_ONE_WAY,不等待 BR_REPLY
  2. 驱动层:binder_transaction 中设置 TF_ONE_WAY 标志,不创建回复事务。
  3. Server 端:BBinder::onTransact 正常处理,但返回值被忽略。

oneway 的注意事项:

  • 不能有返回值或 out 参数(reply 为 null)。
  • 调用顺序保证:Binder 驱动保证同一个线程发出的 oneway 调用按顺序到达。
  • 线程池:oneway 调用不会占用 Client 的 Binder 线程(不需要等待回复)。

七、Binder 的安全机制

7.1 UID/PID 验证

// 服务端获取调用者身份
// frameworks/native/libs/binder/IPCThreadState.cpp
uid_t IPCThreadState::getCallingUid() const {
return mCallingUid; // 由驱动在事务开始时设置
}

pid_t IPCThreadState::getCallingPid() const {
return mCallingPid;
}

// 驱动保证这些值的真实性(从 t->from_euid 和 t->from_pid 获取)

7.2 SELinux 对 Binder 的约束

# system/sepolicy/public/service.te
# Binder 服务的 SELinux 控制
type system_server_service, service_manager_type;

# 只有特定域可以向 system_server 注册服务
allow { audioserver cameraserver } system_server_service:service_manager add;

# system/sepolicy/vendor/
# Binder 调用的 SELinux 控制
binder_call(system_server, appdomain) # 允许 appdomain 调用 system_server
binder_call(appdomain, mediaserver) # 允许 mediaserver 调用 appdomain

八、Binder 性能优化与限制

8.1 事务大小限制

Binder 单次事务最大数据量约为 1016KB(1MB - 8KB*2)。超限后会抛出 TransactionTooLargeException

处理大数据的方式:

  1. 使用 ashmem 传递大数据(Binder 只传 fd)。
  2. 分批传输。
  3. 使用 ContentProvider 的 openFile 接口。

8.2 Binder 线程耗尽

如果 Server 端所有 15 个 Binder 线程都在处理耗时操作(如同步网络请求),新的 Binder 请求会在 Server 的 todo 队列中等待,导致 Client 端超时(ANR)。

最佳实践:

  • Binder 线程中避免耗时操作(异步化)。
  • 耗时操作交给后台线程池处理。
  • 使用 oneway 调用减少等待。

九、剑指大厂 — 核心面试题

Q1: Binder 有什么优势? – 《ByteDance》

优点:

  • 内存开辟(一次拷贝):通过 mmap 机制,内核空间和用户空间映射同一块物理内存,数据从 Client 拷贝到内核后,Server 直接通过 mmap 区域读取,无需二次拷贝。传统 IPC(如 Socket)需要两次拷贝(用户空间 -> 内核空间 -> 用户空间)。
  • 风险隔离:Binder 的 C/S 架构天然实现进程隔离。一个 Server 进程崩溃不影响 Client 进程。相反,共享内存方案中,一个进程崩溃可能破坏共享数据结构,导致关联进程异常。
  • 安全验证:Binder 驱动在内核层自动记录调用者的 UID/PID,无法伪造。服务端通过 Binder.getCallingUid() 进行权限验证。配合 SELinux 策略,实现多层安全防护。
  • 对象生命周期管理:Binder 驱动使用强/弱引用计数管理跨进程对象。当 Client 释放引用,驱动自动通知 Server 调整计数。配合 Death Recipient 机制,Client 可以感知 Server 死亡。

Q2: Binder 是如何做到一次拷贝的? – 《Tencent》

内存划分:内存被操作系统分成两块:用户空间和内核空间。用户空间是用户程序代码运行的地方,内核空间是系统内核运行的地方,可以共享。为了安全,这两者是隔离的,这样即使用户的应用程序崩溃也不会影响到内核。

在 32 位系统(2^32,即总共可访问地址为 4G)中,内核 1G,用户空间 3G。在 64 位系统中,低位 0-47 位才是有效的可变地址(寻址空间 256T),高位 48-63 位全补 0 或 1。一般高位全补 0 对应的地址空间是用户空间,高位全补 1 对应的是内核空间。

Binder 一次拷贝的具体过程:

1. ProcessState::self() 时 mmap 一块物理内存
用户空间地址 = mmap(..., driverFD, 0);
内核空间地址 = proc->alloc.buffer (驱动层面)

2. Client 发送数据:
ioctl(BINDER_WRITE_READ)
→ binder_thread_write()
→ binder_transaction()
→ copy_from_user(t->buffer, user_data) // 【唯一的一次拷贝】
// t->buffer 在目标进程的内核缓冲区中
// 而目标进程通过 mmap 映射了同一块物理内存

3. Server 接收数据:
ioctl(BINDER_WRITE_READ)
→ binder_thread_read()
→ 返回 BR_TRANSACTION 给用户空间
→ Parcel 中的数据直接指向 mmap 区域
// Server 无需再经历 copy_to_user

关键:Binder 驱动分配的缓冲区位于目标进程的 mmap 区域内,所以从用户空间拷贝到内核后,目标进程的用户空间可以直接读取,无需第二次拷贝。

Q3: mmap 的原理讲解 – 《Tencent》

MMAP:Linux 通过将一个虚拟内存区域与一个磁盘上的对象(或匿名内存)关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。

对文件进行 mmap,会在进程的虚拟内存分配地址空间,建立映射关系。实现这样的映射关系后,就可以对这一段内存采用指针的方式进行读写操作,而系统会自动回写到对应的文件磁盘上。

mmap 在 Binder 中的作用:

  1. 映射一块内核缓冲区到用户空间,实现零拷贝数据共享。
  2. 大小限制为 1MB - 8KB(约 1016KB),这是单次 Binder 事务的最大数据量。
  3. mmap 是 lazy allocation 的——映射时只建立 VMA,不分配物理页面。只有真正访问时(通过 copy_from_user)才通过 page fault 分配物理页。

Q4: oneway 调用和同步调用的区别是什么?内部如何实现?

区别:

特性 同步调用 oneway 调用
Client 是否等待 是(阻塞) 否(立即返回)
是否有返回值 无(void)
线程占用 Client 线程阻塞 Client 线程不阻塞
调用顺序 严格串行 同一 Client 线程保证串行
驱动处理 需要 BR_REPLY 返回 BR_TRANSACTION_COMPLETE 即完成

内部实现:

  1. AIDL 层:FLAG_ONEWAY 标志,reply 为 null。
  2. IPCThreadState 层:检测 TF_ONE_WAY 标志,不调用 waitForResponse。
  3. 驱动层:binder_transaction 设置 TF_ONE_WAY,不创建回复事务,直接给 Client 发 BR_TRANSACTION_COMPLETE

Q5: Binder 线程池为什么要限制 15 个线程?怎么配置?

限制 15 个线程的原因:

  1. 防止线程爆炸:每个 Binder 线程占用一个内核 task_struct 和用户空间栈(约 1MB),过多线程会耗尽内存。
  2. 调度效率:线程过多导致频繁上下文切换,降低吞吐量。
  3. 任务粒度:Binder 事务通常很小(几十微秒到几毫秒),15 个线程已经足够处理大部分并发情况。
  4. 历史原因:早期 Android 设备(512MB RAM)资源有限,15 是一个平衡的选择。

配置方式:

// 应用层无法直接修改,但可以通过 JNI 在 native 层调用
ProcessState::self()->setThreadPoolMaxThreadCount(20);

// 或者在启动时
sp<ProcessState> ps = ProcessState::self();
ps->startThreadPool();
ps->giveThreadPoolName();

注意:不同厂商的 ROM 可能修改了默认值,测试时要考虑兼容性。

核心参考 AOSP 路径:

  • drivers/android/binder.c — Binder 驱动(Android 10+)
  • frameworks/native/libs/binder/ProcessState.cpp — 进程单例
  • frameworks/native/libs/binder/IPCThreadState.cpp — 线程级通信
  • frameworks/native/libs/binder/BpBinder.cpp — Client 代理
  • frameworks/native/libs/binder/BBinder.cpp — Server 桩
  • frameworks/native/libs/binder/Parcel.cpp — 数据封装
  • frameworks/native/cmds/servicemanager/ServiceManager.cpp — 服务管理
  • frameworks/base/core/java/android/os/Binder.java — Java 层 Binder
  • frameworks/base/core/java/android/os/BinderProxy.java — Java 层 Binder 代理
  • system/tools/aidl/ — AIDL 代码生成工具
打赏
  • 微信
  • 支付宝

评论