Binder 是 Android 的核心 IPC 机制,运行在 Linux 内核空间。从最底层的 /dev/binder 字符设备,到内核中的 binder_proc/binder_thread/binder_node/binder_ref 四元组数据结构,再到著名的”一次拷贝”机制,Binder 驱动的设计体现了嵌入式场景下对效率、安全和简洁性的极致追求。本文深入内核层,基于 Android 通用内核(common kernel)的 Binder 驱动源码进行全面分析。
一、Binder 驱动的整体架构
1.1 Linux 内核中的位置
用户空间 ┌──────────────┐ ┌──────────────┐ │ Client 进程 │ │ Server 进程 │ │ │ │ │ │ BpBinder │ │ BBinder │ │ (Binder 代理)│ │ (Binder 实现)│ │ ↓ │ │ ↑ │ │ ioctl() │ │ ioctl() │ └──────┬───────┘ └──────┬───────┘ │ │ ═══════╪═══════════════════════════════╪════════ │ Linux Kernel │ └───────┬───────────────────────┘ │ ┌──────────▼──────────┐ │ /dev/binder 设备 │ │ binder_ioctl() │ │ binder_ioctl_write_read()│ └──────────┬──────────┘ │ ┌──────────▼──────────┐ │ binder_transaction │ ← "一次拷贝" 的核心 └─────────────────────┘
|
AOSP 内核源码路径:在 Android 通用内核(common kernel)中:
drivers/android/binder.c — Binder 驱动主文件
drivers/android/binder_alloc.c — Binder 内存分配
drivers/android/binder_trace.h — Binder trace 点
Android 用户空间 Binder 库:
frameworks/native/libs/binder/ — libbinder (C++ 用户空间库)
Binder.cpp / BpBinder.cpp / IPCThreadState.cpp / Parcel.cpp
1.2 Binder 的 misc 字符设备
Binder 驱动在 Linux 中注册为 misc 字符设备:
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, };
static struct miscdevice binder_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "binder", .fops = &binder_fops };
static int __init binder_init(void) { ret = misc_register(&binder_miscdev); binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); }
|
二、核心数据结构四元组
Binder 驱动用四个核心数据结构来描述所有通信实体。
2.1 binder_proc——进程上下文
每个使用 Binder 的进程在驱动中对应一个 binder_proc 结构:
struct binder_proc { struct hlist_node proc_node; struct rb_root threads; struct rb_root nodes; struct rb_root refs_by_desc; struct rb_root refs_by_node; struct list_head waiting_threads; int pid; struct task_struct *tsk; struct list_head todo; wait_queue_head_t wait; struct binder_alloc alloc; int max_threads; int requested_threads; int requested_threads_started; };
|
当进程调用 open("/dev/binder") 时触发 binder_open():
static int binder_open(struct inode *nodp, struct file *filp) { struct binder_proc *proc; proc = kzalloc(sizeof(*proc), GFP_KERNEL); proc->tsk = current->group_leader; INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); binder_alloc_init(&proc->alloc); filp->private_data = proc; hlist_add_head(&proc->proc_node, &binder_procs); return 0; }
|
2.2 binder_thread——线程上下文
每个在 Binder 驱动中工作的线程对应一个 binder_thread:
struct binder_thread { struct binder_proc *proc; struct rb_node rb_node; struct list_head waiting_thread_node; int pid; int looper; struct binder_transaction *transaction_stack; struct list_head todo; struct binder_error return_error; wait_queue_head_t wait; bool looper_need_return; };
|
binder_thread 中的 transaction_stack 维护了一个 RPC 调用栈,记录了从当前事务回溯到最外层事务的完整路径。这对理解 Binder 的事务嵌套至关重要——当 Binder 调用链是 A→B→C 时,C 中的 transaction_stack 指向 B 发给 C 的事务,该事务的 from_parent 又指向 A 发给 B 的事务。
2.3 binder_node——Binder 实体
每个 Binder 对象(即 BnXXX 服务端)在驱动中有一个 binder_node。这是 Service 端暴露的实体的内核表示:
struct binder_node { int debug_id; struct binder_work work; union { struct rb_node rb_node; struct hlist_node dead_node; }; struct binder_proc *proc; struct hlist_head refs; int internal_strong_refs; int local_weak_refs; int local_strong_refs; int tmp_refs; binder_uintptr_t ptr; binder_uintptr_t cookie; };
|
关键点:ptr 和 cookie 分别存储了用户空间 BBinder 对象的弱指针地址和实际指针。驱动通过 binder_node 将物理地址映射为跨进程的逻辑实体。
2.4 binder_ref——Binder 引用
每个进程对远程 Binder 实体的引用对应一个 binder_ref。这是 Client 端持有的代理:
struct binder_ref { int debug_id; struct rb_node rb_node_desc; struct rb_node rb_node_node; struct hlist_node node_entry; struct binder_proc *proc; struct binder_node *node; uint32_t desc; int strong; int weak; struct binder_ref_death *death; };
|
每个 binder_ref 有一个 desc(通常称为 handle),在目标进程内是唯一的。当 Client 通过 Binder 发送请求时,它使用的是 handle(如 handle=1 对应 ServiceManager),驱动在内部通过 binder_ref 找到对应的 binder_node,进而定位到 Server 进程。
三、ioctl 命令体系
3.1 binder_ioctl——所有操作的总入口
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct binder_proc *proc = filp->private_data; void __user *ubuf = (void __user *)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: ret = binder_ioctl_set_ctx_mgr(filp); break; case BINDER_THREAD_EXIT: binder_thread_exit(proc, thread); break; case BINDER_VERSION: break; } return ret; }
|
3.2 BINDER_WRITE_READ——最核心的命令
这是 Binder 中使用频率最高的 ioctl,几乎所有通信(事务发送、回复、ACK、死亡通知)都通过它完成:
static int binder_ioctl_write_read(struct file *filp, unsigned int cmd, unsigned long arg, struct binder_thread *thread) { struct binder_write_read bwr;
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; }
if (bwr.write_size > 0) { ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); }
if (bwr.read_size > 0) { ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); }
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { ret = -EFAULT; } return ret; }
|
binder_write_read 结构的定义:
struct binder_write_read { binder_size_t write_size; binder_size_t write_consumed; binder_uintptr_t write_buffer; binder_size_t read_size; binder_size_t read_consumed; binder_uintptr_t read_buffer; };
|
四、BC_TRANSACTION / BR_TRANSACTION——事务流转
4.1 BC_TRANSACTION 的发送端处理
当 Client 发起 Binder 调用时,用户空间构造 BC_TRANSACTION 命令,通过 ioctl 写入驱动。驱动在 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) { while (ptr < end && thread->return_error.cmd == BR_OK) { get_user(cmd, (uint32_t __user *)ptr); switch (cmd) { case BC_TRANSACTION: case BC_REPLY: { struct binder_transaction_data tr; copy_from_user(&tr, ptr, sizeof(tr));
binder_transaction(proc, thread, &tr, cmd == BC_REPLY, 0); break; } } } }
|
4.2 binder_transaction——核心事务处理
这是 Binder 驱动的灵魂——所有跨进程数据传递都在这里完成:
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply, binder_size_t extra_buffers_size) { if (reply) { in_reply_to = thread->transaction_stack; target_proc = in_reply_to->from->proc; target_thread = in_reply_to->from; target_node = NULL; } else { if (tr->target.handle) { ref = binder_get_ref_olocked(proc, tr->target.handle, true); target_node = ref->node; target_proc = target_node->proc; } else { target_node = binder_context_mgr_node; target_proc = target_node->proc; } }
t = kzalloc(sizeof(*t), GFP_KERNEL);
t->buffer = binder_alloc_buf(&target_proc->alloc, tr->data_size, tr->offsets_size, extra_buffers_size, !reply && (t->flags & TF_ONE_WAY));
copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size);
off_end = (void *)off_start + tr->offsets_size; for (; offp < off_end; offp++) { struct flat_binder_object *fp; fp = (struct flat_binder_object *)(t->buffer->data + *offp); switch (fp->hdr.type) { case BINDER_TYPE_BINDER: ret = binder_translate_binder(fp, t, thread); break; case BINDER_TYPE_HANDLE: ret = binder_translate_handle(fp, t, thread); break; case BINDER_TYPE_FD: ret = binder_translate_fd(fp, t, thread); break; } }
if (target_thread) { binder_enqueue_thread_work(target_thread, &t->work); wake_up_interruptible_sync(&target_thread->wait); } else { binder_enqueue_work_ilocked(&t->work, &target_proc->todo); wake_up_interruptible(&target_proc->wait); } }
|
4.3 BR_TRANSACTION 的接收端处理
目标进程在 binder_thread_read 中等待并获取事务:
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block) { while (1) { if (!list_empty(&thread->todo)) { w = list_first_entry(&thread->todo, struct binder_work, entry); } else if (!list_empty(&proc->todo)) { w = list_first_entry(&proc->todo, struct binder_work, entry); } else { if (non_block) { ret = -EAGAIN; goto out; } wait_event_freezable_exclusive(proc->wait, binder_has_work(thread)); }
switch (w->type) { case BINDER_WORK_TRANSACTION: { t = container_of(w, struct binder_transaction, work); if (!(t->flags & TF_ONE_WAY)) { t->to_parent = thread->transaction_stack; t->to_thread = thread; thread->transaction_stack = t; } } } } }
|
五、一次拷贝的奥秘
5.1 传统 IPC 的数据拷贝次数
传统 IPC(如 SysV 消息队列、Unix Domain Socket)至少需要两次拷贝:
- 发送者用户空间 → 内核缓冲区
- 内核缓冲区 → 接收者用户空间
如果数据量大,两次拷贝加上上下文切换成为性能瓶颈。
5.2 Binder 的一次拷贝原理
Binder 实现一次拷贝的关键在于 mmap + 内核在接收者地址空间分配缓冲区:
步骤1:Server 进程初始化时,通过 mmap 将内核缓冲区映射到用户空间 binder_mmap(proc, vma) → 在内核中分配物理内存页 → 映射到 Server 进程的用户空间地址
步骤2:Client 发起 Binder 调用时: binder_transaction() → binder_alloc_buf(&target_proc->alloc, data_size, ...) (在目标进程(Server)的 mmap 区域中分配缓冲区) → copy_from_user(t->buffer->data, client_user_buf, data_size) (从 Client 用户空间拷贝到内核空间 —— 同时也是 Server 的 mmap 区域)
步骤3:Server 收到 BR_TRANSACTION 后: 直接读取自己的 mmap 区域,无需额外拷贝! Parcel 数据已经在内核中 → 同时也映射在 Server 的进程地址空间
|
struct binder_buffer *binder_alloc_buf(struct binder_alloc *alloc, binder_size_t data_size, binder_size_t offsets_size, binder_size_t extra_buffers_size, int is_async) { }
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { struct binder_proc *proc = filp->private_data;
if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M;
proc->alloc.buffer = (void *)vma->vm_start; alloc->pages = kvmalloc_array(...); for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); alloc->pages[i] = page; vm_insert_page(vma, (unsigned long)alloc->buffer + i * PAGE_SIZE, page); } return 0; }
|
关键点总结:
- 内核只做一次 copy_from_user,数据从发送者用户空间进入内核页。
- 接收者通过 mmap 可以直接访问这些内核页(同一块物理内存映射到了不同的虚拟地址空间)。
- 因此,Binder 完成了 一次拷贝 的跨进程通信。
六、Binder 线程池模型
Binder 支持 Service 端多线程处理。驱动管理如下:
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) { switch (cmd) { case BC_REGISTER_LOOPER: thread->looper |= BINDER_LOOPER_STATE_REGISTERED; break; case BC_ENTER_LOOPER: thread->looper |= BINDER_LOOPER_STATE_ENTERED; break; case BC_EXIT_LOOPER: thread->looper |= BINDER_LOOPER_STATE_EXITED; break; } }
static int binder_thread_read(...) { if (proc->requested_threads + proc->requested_threads_started < proc->max_threads && (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) && !list_empty(&proc->todo)) { proc->requested_threads++; put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer); } }
|
默认最大线程数为 15(proc->max_threads),可通过 BINDER_SET_MAX_THREADS ioctl 调整。ServiceManager 设置为 0(不需要额外线程池)。
七、死亡通知机制
Binder 支持死亡通知(Death Recipient),当 Service 端进程意外退出时,Client 端可以收到通知。
case BC_REQUEST_DEATH_NOTIFICATION: { uint32_t target; binder_uintptr_t cookie; struct binder_ref *ref; struct binder_ref_death *death;
get_user(target, (uint32_t __user *)ptr); get_user(cookie, (binder_uintptr_t __user *)(ptr + sizeof(uint32_t)));
ref = binder_get_ref_olocked(proc, target, false); death = kzalloc(sizeof(*death), GFP_KERNEL); death->cookie = cookie; ref->death = death;
if (ref->node->proc == NULL) { binder_enqueue_work(proc, &death->work, &thread->todo); } }
static int binder_release(struct inode *nodp, struct file *filp) { struct binder_proc *proc = filp->private_data; binder_deferred_release(proc); }
static void binder_send_dead_binder(struct binder_proc *proc, struct binder_ref *ref) { binder_enqueue_work(proc, &death->work, &proc->todo); }
|
八、Binder 协议版本演进
Binder 协议有多个版本,通过 BINDER_VERSION ioctl 查询:
| 版本 |
特性 |
| 7 (Linux 3.x-4.x) |
基础 Binder,无 scatter-gather |
| 8 (Linux 4.14+) |
引入 scatter-gather I/O(BINDER_TYPE_PTR 支持跨进程引用嵌套数据) |
| 9 (Linux 5.4+) |
引入 binderfs,支持多 Binder 实例和命名空间隔离 |
Android 内核通常使用版本 8 的 binder。
九、核心面试题
Q1:Binder 的”一次拷贝”到底是怎么实现的?为什么传统 IPC 需要两次?
传统 IPC 的数据路径:发送者用户空间 → 内核通用缓冲区 → 接收者用户空间(两次 copy_from_user/copy_to_user)。Binder 的优化在于:Server 进程通过 mmap 将内核分配的物理页直接映射到自己的用户空间。当 Client 发送数据时,驱动在 Server 的 mmap 区域中分配缓冲区(binder_alloc_buf),然后做一次 copy_from_user 将 Client 数据拷入这块区域。由于这块区域同时映射在 Server 的用户空间,Server 可以直接读取,无需第二次拷贝。
Q2:Binder 的 binder_node 和 binder_ref 设计解决了什么核心问题?
这是 Binder 的”能力安全模型”的实现基础。binder_node 是”能力”的实体端(Server 端),binder_ref 是”能力”的引用端(Client 端)。当 Client 通过 Binder 传递一个 handle 给另一进程时,驱动自动在目标进程创建新的 binder_ref 指向同一个 binder_node。这意味着:进程只能使用显式授予给它的 Binder 引用,无法伪造或猜测 handle,天然实现了细粒度的权限控制。这与传统的 Unix 权限模型(基于 UID/GID 的水平权限)完全不同。
Q3:Binder 如何处理递归和重入?transaction_stack 的作用是什么?
Binder 支持同步调用链(A 调 B,B 调 C),这种情况下 A 的 Binder 线程在等待 B 的回复,而 B 的线程又被 C 占用。transaction_stack 维护了调用栈关系:每个同步调用的 binder_transaction 通过 from_parent 和 to_parent 指针链接,形成一条调用链。当 C 回复时,驱动根据 transaction_stack 找到 B 的等待线程,唤醒其处理回复。线程优先级继承(priority inheritance)也依赖 transaction_stack 在调用链上传播。
AOSP 核心路径参考:
drivers/android/binder.c — Binder 驱动核心
drivers/android/binder_alloc.c — Binder 内存分配器
include/uapi/linux/android/binder.h — Binder 协议头文件
frameworks/native/libs/binder/Binder.cpp
frameworks/native/libs/binder/BpBinder.cpp
frameworks/native/libs/binder/IPCThreadState.cpp
frameworks/native/libs/binder/Parcel.cpp
frameworks/native/libs/binder/ProcessState.cpp