39. 与 Socket 对比:Binder 与 Unix Domain Socket 在性能与安全上的对比

这个问题,说实话,是面试中非常高频的一道题。很多候选人能把 Binder 的原理讲得头头是道,但一问到「为什么 Android 不用 Socket 而用 Binder」,就卡住了。

我个人觉得,理解这个对比,才算真正理解了 Binder 的设计哲学。今天我们就来掰开揉碎聊一聊。

先聊聊 Unix Domain Socket

Unix Domain Socket(简称 UDS)不是什么新鲜东西。它从 Unix 早期就有了,专门用于同一台机器上的进程间通信。

它的工作方式很简单:一个进程创建 socket 文件,另一个进程连接上去。数据通过内核拷贝,走的是文件系统路径。

我早期做嵌入式开发时,经常用 UDS 做进程间通信。那时候觉得它挺方便的,API 和网络 socket 一样,上手快。

但后来做 Android 系统开发,我发现 UDS 有几个硬伤。

性能对比:Binder 为什么更快?

我们先看一张对比图,这是我画的核心流程差异:

Binder vs Unix Domain Socket 数据传输流程对比 Binder 流程 进程 A(客户端) 进程 B(服务端) 一次拷贝 内核空间(Binder 驱动) ✅ 一次内存拷贝 ✅ 支持 Binder 线程池 ✅ 自动引用计数管理 ✅ 支持 RPC 调用语义 ✅ 支持死亡通知 Unix Domain Socket 流程 进程 A(客户端) 进程 B(服务端) 两次拷贝 内核空间(Socket 缓冲区) ❌ 两次内存拷贝 ❌ 需要维护 socket 文件 ❌ 无 RPC 语义 ❌ 无自动引用管理 ❌ 无死亡通知机制

从图上可以清楚看到,Binder 只需要一次内存拷贝,而 UDS 需要两次。

为什么会这样?

Binder 利用内核的 mmap 机制,在进程 A 和内核之间共享了一块内存。数据从进程 A 拷贝到内核后,进程 B 直接映射这块内存,不需要再拷贝一次。

UDS 就不一样了。数据先从进程 A 拷贝到内核的 socket 缓冲区,再从内核缓冲区拷贝到进程 B。两次拷贝,性能自然差一些。

我在项目中做过实测。传输 1KB 的小数据包,Binder 比 UDS 快大约 30%。传输 1MB 的大数据,差距更明显,Binder 能快 50% 以上。

核心结论: Binder 的一次拷贝设计,在性能上天然优于 UDS 的两次拷贝。对于 Android 这种频繁进行 IPC 的系统,这个优势非常关键。

安全对比:Binder 为什么更安全?

安全这块,差距就更大了。

UDS 的安全模型,说白了就是文件权限。你创建一个 socket 文件,设置 0777 权限,谁都能连。设置 0700,只有自己能用。就这么简单粗暴。

但 Android 的场景要复杂得多。系统服务、应用进程、native 进程,各种身份混在一起。光靠文件权限根本不够用。

Binder 的安全机制,我总结为三层:

安全层级 Binder 实现 UDS 对应
身份识别 自动获取 PID/UID,不可伪造 需要手动获取,可伪造
权限校验 基于 SELinux 策略,细粒度控制 仅文件权限,粗粒度
数据隔离 内核级校验,应用无法绕过 依赖应用自身实现

我曾经在项目中遇到过一个问题:某个系统服务用 UDS 暴露接口,结果被恶意应用通过猜测 socket 路径的方式连接上了,导致数据泄露。后来改成 Binder,配合 SELinux 策略,才彻底堵住这个漏洞。

避坑指南: 我曾经见过有人为了「省事」,在系统服务中混用 UDS 和 Binder。结果安全审计时发现 UDS 接口没有做身份校验,差点出事故。我的建议是:Android 平台上,能用 Binder 的地方就别用 UDS。

功能对比:Binder 的额外优势

除了性能和安全,Binder 还有一些 UDS 没有的功能:

  • RPC 语义: Binder 天然支持远程过程调用。你调用一个接口,就像调用本地方法一样。UDS 需要自己封装请求和响应,解析协议,非常麻烦。
  • 死亡通知: Binder 可以监听服务端是否挂掉。服务端 crash 了,客户端能收到通知。UDS 没有这个机制,需要自己心跳检测。
  • 引用计数: Binder 自动管理 Binder 对象的引用计数。UDS 没有这个概念,需要自己维护。
  • 线程池管理: Binder 驱动自带线程池,自动分配线程处理请求。UDS 需要自己管理线程。

你想想看,如果 Android 用 UDS 实现 ServiceManager,每次获取服务都要自己解析协议、处理并发、管理连接,那代码得写成什么样?

什么时候该用 UDS?

说了这么多 Binder 的优势,但 UDS 也不是一无是处。我在某些场景下还是会用 UDS:

  • 跨平台通信: 如果你的代码需要跑在 Linux、macOS 上,UDS 是标准 POSIX 接口,移植性好。Binder 是 Android 特有的。
  • 大数据传输: Binder 对单次传输数据量有限制(默认 1MB),超过这个量会报错。UDS 没有这个限制,适合传输大文件。
  • 简单场景: 如果只是两个进程之间传个字符串,不需要复杂的权限控制,UDS 反而更轻量。

个人经验: 我在做多媒体系统时,传输视频帧数据就用 UDS。因为帧数据量大,而且不需要复杂的权限控制。但控制命令走 Binder,因为需要精确的权限管理和 RPC 语义。两者各司其职,效果很好。

总结

Binder 和 UDS 的对比,核心就三点:

  • 性能: Binder 一次拷贝 vs UDS 两次拷贝,Binder 胜出
  • 安全: Binder 多层安全机制 vs UDS 文件权限,Binder 完胜
  • 功能: Binder 自带 RPC、死亡通知、线程池,UDS 需要自己实现

说白了,Android 选择 Binder 而不是 UDS,不是偶然的。它是为了满足移动系统对性能、安全、功能的高要求而设计的。理解了这个对比,你才算真正理解了 Binder 的设计初衷。


公众号:蓝海资料掘金营,微信deep3321