源码基于:Android R
0. 前言
在Linux 系统中现有的进程间通信(IPC)方式:
- 管道(PIPE):在创建时分配一个page大小的内存,缓存区大小比较有限;
- 命名管道(FIFO):考虑 PIPE_BUF 和原子操作;
- 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
- 共享内存: 无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
- 套接字: 作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
- 信号量: 常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段;
- 信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;
0.1 使用Binder 原因
0.1.1 性能
Socket 作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。
消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。
共享内存虽然无需拷贝,但控制复杂,难以使用。
Binder 只需要一次数据拷贝,性能上仅次于共享内存。
0.1.2 稳定性
Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。共享内存虽然无需拷贝,但是控制负责,难以使用。
从稳定性的角度讲,Binder 机制是优于内存共享的。
0.1.3 安全性
Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。同时 Binder 既支持实名 Binder,又支持匿名 Binder,安全性高。
2. Binder 划分
在Android 8.0 之前,Binder机制比较简单,只有一个驱动设备"/dev/binder",一个守护进行"/system/bin/servicemanager",一个binder库"/system/lib64/libbinder.so".
在Android 8.0开始,Android引入了Treble的机制,为了方便Android系统的快速移植、升级,提升系统稳定性,Binder机制被拓展成了"/dev/binder", "/dev/hwbinder","/dev/vndbinder"。
我们原先使用的"/dev/binder",成为框架进程的专有节点,这意味着供应商进程无法再访问此节点。供应商进程可以访问 /dev/hwbinder,但必须将其 AIDL 接口转为使用 HIDL。
对于想要继续在供应商进程之间使用 AIDL 接口的供应商,需要使用 /dev/vndbinder(而非 /dev/binder)。
Android8.0 及之后的Binder域如下图所示:
3. 三种 binder 介绍
3.1 vndbinder 和 binder
vnbinder 和binder 使用的是一个ServiceManager 和libbinder,只不过在选择的时候会区分open /dev/binder 还是/dev/vnbinder:
frameworks/native/cmds/servicemanager/main.cpp
int main(int argc, char** argv) {
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
}
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
..
}
代码中根据参数选择性的将driver 传入进行open,详细看ProcessState::initWithDriver() 函数。
通常,供应商进程不直接打开 Binder 驱动程序,而是链接到打开 Binder 驱动程序的 libbinder 用户空间库。为 ::android::ProcessState() 添加方法可为 libbinder 选择 Binder 驱动程序。供应商进程应该在调用 ProcessState、IPCThreadState 或发出任何普通 Binder 调用之前调用此方法。要使用该方法,请在供应商进程(客户端和服务器)的 main() 后放置以下调用:
ProcessState::initWithDriver("/dev/vndbinder");
dev/binder和dev/vndbinder无法在一个进程中同时使用
binder和vndbiner 的机制共用一套libbinder,因此两者使用时,每次只能指定一个设备节点,不能同时使用。
3.2 hwbinder
hwbinder 独立于binder和vndbinder,拥有独立的驱动设备 /dev/hwbinder,独立的hwservicemanager (system/hwservicemanager/ 目录) 和独立的binder 库libhwbinder (system/libhwbinder/ 目录)。
android 8.0 以后采用了treble 的架构,framework 和HAL 是独立的,在不同的 fw 和 HAL 进程中,进程间通信使用的是 HIDL 语言,而不在使用 AIDL 语言,因此使用了不同的 binder 驱动设备
3.3 binder 库的变化
binder | vndbinder | hwbinder | |
lib binder 位置 | frameworks/native/libs/binder | frameworks/native/libs/binder | system/libhwbinder |
service manager 位置 | frameworks/native/cmds/servicemanager | frameworks/native/cmds/servicemanager | system/hwservicemanager |