之前也有讲过Android为什么不用Linux的IPC而实现了自己的Binder机制,今天来脑补一下Binder的模型~
本篇博客部分内容摘自Binder Communication and Discovery
在Android系统中,为了安全性进程和进程之间一般是相互独立的。那么,进程间通信需要如何进行呢?Android中进程间通信的方式一般分为四种:Intent、Broadcast、AIDL、Content Provider。实际上它们都是通过Binder机制来完成的。
我们知道,Android系统是基于Linux的。而在Linux中,每个进程都拥有独自的4G虚拟内存空间,其中3-4G是内核空间,0-3G为用户空间。用户空间之间的数据是不同享的,而内核空间进程之间是公用的。所以可想而知,要想进程间通信则要通过内核空间。而Binder就是在内核空间的一个字符设备驱动(dev/binder)。
下面我们通过一系列的图,来逐级探究Binder的模型:
对于客户端进程而言,它只是想使用Service服务进程:
但对进程而言,又不能直接在其他进程直接调用操作(如Read/Write)数据。但是内核空间可以,所以开发者就在Kernel层实现了一个Binder驱动,使用这个驱动来进行交互:
NOTE:因为可能会有多个客户端去访问Service端,所以Binder驱动要实现同步的状态。
Binder设备驱动存在于dev/binder,并且提供了一个相对简单的API基于open、release、poll、mmap、flush和ioctl操作。事实上,大多数通信都通过的是 ioctel(binderFd, BINDER_WRITE_READ ,&bwd)。
bwd定义:
struct binder_write_read {
signed long write_size; /* bytes to write */
signed long write_consumed; /* bytes consumed by driver */
unsigned long write_buffer;
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer;
};
客户端与服务端通信的过程中,包含了binder令牌,要调用的函数,原始数据buffer以及发送方PID/UID。
大多数常用的操作,都被封装在libbinder库中。
客户端和服务端不想知道关于Binder协议和libbinder中的操作,所以开发者就发明了proxy和stub来抽象底层的操作:
NOTE:proxy和stub可以通过AIDL语言由工具自动生成。
实际上,客户端根本不想知道他们正在使用IPC,更不关心Binder或者proxy。所以开发者依靠一个manager来抽象所有的复杂性操作:
NOTE:这一点,System Services尤其是这样, 通常它们之暴露一些API给客户端的manager。
但是客户端是如何得到他们想要通信的服务端的句柄的呢?答案是sevicemanager,并且希望想通信的服务端已经注册在servicemanager上了:
NOTE:出于安全/稳定的原因,Binder驱动只接受一次性的注册。这就是为什么servicemanager是Android最早启动的服务了。
下面列出了一些已经注册在servicemanager上的服务:
至此,Binder的模型就描述完了。其中如果有什么不对的地方还请大家指出来,大家共同成长~