Binder 是 Android 中实现的进程间通信。在操作系统内核层面,有 Binder 驱动提供支持。
Android 中存在众多系统服务(Service),分别运行在不同的进程中,而应用程序在底层通过 Binder 与这些服务进行交互。
Binder 机制可以通过 共享内存 实现。
每个进程可以像打开文件一样打开 Binder “设备”,而每个进程最多只能打开一个 Binder,其子线程共享该 Binder。
Android 中存在一个 ServiceManager 进程,用于管理其他所有的服务,其自身也是一个服务。
一个进程先获取到 ServiceManager 的 IBinder,再通过 ServiceManager 获得目标服务的 IBinder。
假设一个应用程序 example 想查询当前的 wifi 列表,example 只需要调用 Android Framework 提供的一系列 API 进行操作,拿到 WifiManager 的 IBinder,最终通过 Binder 的 transact
函数执行对应操作。
具体过程如下:
Binder.transact(int code, Parcel data, Parcel reply, 其他参数...)
应用程序将使用一个 code
来告诉服务端执行哪个操作,而 data
则是这个操作的参数,由应用程序写入。Binder.transact
可以理解为一个系统调用,此时陷入操作系统内核,执行 Binder 驱动,将操作请求转发给 Wifi 服务进程,然后挂起当前应用程序的进程,以等待 Wifi 服务进程处理完这个操作请求。当 Wifi 服务进程处理完操作,会将返回数据写入 reply
(写入过程可以是共享内存的方式),然后唤醒应用程序的进程,应用程序从 reply
中取出数据。
在这个过程中,需要对 data
、reply
反复进行装包和解包,过程比较繁琐,因此,通常会使用 AIDL 工具自动生成对应的接口、实现及代理。
共享内存:
假设客户端进程C,想要访问服务进程S的数据。
由于进程隔离机制,客户端C并不能直接访问进程S的数据,可以认为进程拥有自己独立的地址空间。
此时,客户端C通过一个 ServiceManager 间接获取到一个 IBinder 对象的引用,借助 Binder 驱动申请了一段内存,将 C 进程中一部分虚拟地址映射到这段内存,同时也将 S 进程中一部分虚拟地址映射到这部分内存,此时,S 将数据写入这部分新分配的内存后,C 进程也能访问这部分内存,实现了共享内存。
这里的内存映射:
C 进程拥有的对象 IBinder,在 C 进程中的虚拟内存地址假设为 0x7777,而 S 进程拥有的 Binder 对象在 S 进程中的虚拟地址为 0xAAAAA,但实际上两者所指向的是同一块物理内存。虽然二者实际都是访问同一块物理内存,但二者拥有的权限不同。
内存映射的方式减少了内存数据冗余和复制操作的开销。