要理解binder,先要知道IPC,Inter-process communication ,也就是进程中相互通信,Binder是Android提供的一套进程间相互通信框架。用来多进程间发送消息,同步和共享内存。
Linux中进程通信的方式主要有:
管道:在创建时分配一个page大小的内存,缓存区大小比较有限,速度慢;
消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
套接字:作为更通用的接口,传输效率低,主要用于不通机器或跨网络的通信;
信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
信号: 不适用于信息交换,更适用于进程中断控制,比如非法内存访问,杀死某个进程等;统的进程间通信可能会增加进程的开销,而且有进程过载和安全漏洞等方面的风险,Binder
Android系统中的Binder框架图如下:
拿Activity举例从上图可以看出来:Activity是由ActivityManager来控制的,而ActivityManager其实是通过Binder获取ActivityManagerService服务来控制Activity的,并且ActivityManager是Android系统FrameWork层的,和应用中的activity不是同一个进程。
重点:
发送:首先会将要发送的数据放到内存缓存区,通过系统调用进入内核态,内核空间会开辟一块缓存区,通过copyfromuser()将用户空间的数据拷贝到内核空间的缓存区。
接收:接收方的用户进程里,开辟一块内存缓存区,内核调用copytouser()将数据从内核缓存区拷贝到接收进程开辟的内存缓存区。
以上便完成了一次进程间的通信,但是存在下面的不足:
在性能上,需要两次的数据拷贝:用户->内核、内核->用户
由于接收的用户进程无法知道接收的数据多大,所以它只能开辟尽可能大的内存缓存区来接收数据,这样很可能会造成空间的浪费。
通过Binder进行进程间通信的低层依赖的是Binder驱动,但是Binder驱动不是Linux内核的一部分,需要通过动态内核可加载模块的机制,将Binder驱动动态添加到内核空间,来实现Binder通信。
内存映射是通过mmap()来实现的,内存映射是将用户的内存空间映射到内核空间,当内存空间里有进行修改时,则内核空间映射到内存区域也能直接感知到,当内核空间修改时,用户空间也能够直接感知到。内存映射可以减少数据拷贝到次数,实现用户空间与内核空间的高效交互。
内存映射在一次Binder进程间通信的应用是这样的:
Server通过Binder驱动向ServiceManager注册Binder,驱动为这个Binder创建一个在内核中的节点以及将其引用和名字打包传给ServiceManager,ServiceManager就可以把它们填到表中。
Client通过Binder驱动,用Binder名字去ServiceManager中获取对应的Binder引用,通过该引用,就可以使用Server提供的服务。
我们可能还有一个疑问:俩个进程间是没能够直接使用彼此的对象,Binder中是如何实现的呢?
其实Client获取Server的object时,Binder驱动并不会真正地把object传给Client,而是返回一个具有与object一样方法的object代理,它并不具备与object一样真正的能力。当Client调用object代理的A方法时,驱动会查到它对应的是object,就会调用Server中object的A方法,同时将参数也会打包发过去,等待Server中的object的A方法处理好后把结果进行返回,这时Binder驱动会将结果转发给Client,需要注意的是,这一个过程是一个同步的过程,当Server在处理时,Clinet会blok住,所以最好是放在工作线程去调用远程的方法。
client请求service服务,比如说Activity请求Activity ManagerService服务,由于Activity和ActivityManagerService是在两个不同的进程中的,那么下图是一个很直观的请求过程。
但是注意,一个进程是不能直接直接操作另一个进程的,比如说读取另一个进程的数据,或者往另一个进程的内存空间写数据,进程之间的通信要通过内核进程才可以,因此这里就要使用到进程通信工具Binder了如下图:
Binder driver通过/dev/binder /dev/binder 提供了 open, release release, poll poll, mmap mmap, flush flush, and ioctl等操作的接口api。这样进程A和进程B就可以通过内核进程进行通信了。进程中大部分的通信都是通过ioctl(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;
};
但是上面还有个问题就是client和service要直接和binder driver打交道,但是实际上client和service并不想知道binder相关协议,所以进一步client通过添加proxy代理,service通过添加stub来进一步处理与binder的交互。
这样的好处是client和service都可以不用直接去和binder打交道。上面的图好像已经很完善了,但是Android系统更进一步封装,不让client知道Binder的存在,Android系统提供了Manager来管理client。如下图:
这样client只需要交给manager来管理就好了,根本就不用关心进程通信相关的事,关于manager其实是很熟悉的,比如说activity的就是由ActivityManager来控制的,ActivityManager是通过Binder获取ActivityManagerService来控制activity的。这样就不用我们自己来使用Binder来ActivityManagerService通信了。
参考:
https://blog.csdn.net/gqg_guan/article/details/126522292
https://blog.csdn.net/m0_64420071/article/details/125930661