Binder译为中文为黏合剂,在安卓中可以将他理解为黏合两个进程的黏合剂,使得两个进程能够相互通信。
预备知识
一个进程由用户空间和内核空间,用户空间和内核空间是分开的。
在进程间,用户空间的数据不可共享,但内核空间的数据可以共享。
所有进程共享一个内核空间
诶!进程间的内核空间数据是共享的!那么是不是可以通过这个特点来实现用户空间的通信(进程间通信)。
没错,确实可以。
通过调用系统调用函数copy_from_user()和copy_to_user()分别进行从用户空间拷贝数据到内核缓存区和从内核缓存区拷贝数据到用户空间。
通过这个图可以看到,好像确实实现了进程间通信了啊。没用到Binder啊?
Binder的存在即合理。它的存在解决了以上通信方式的弊端。
那么弊端是什么?
1.效率低:因为进行了两次数据的拷贝。
2.接收端不知道要接收多大的数据,因此一般会开比较大的空间;或者先发大小再发数据;但前者浪费空间后者浪费时间。
Binder为何提升了效率?
因为Binder利用了内存映射的概念,只需拷贝一次即可。
为何解决了接收缓存区大小未知的问题?
因为Binder为接收端创造了一个接收缓存区,该接收缓存区与内核缓存区有映射关系。
Binder跨进程通信机制
Binder跨进程通信机制是基于Client-Sever模式。
注册服务
Server进程向Binder驱动发送注册服务请求;
Binder驱动将请求转发给ServiceManager进程;
ServiceManager进程收到请求,添加该Server进程(注册服务);
获取服务
Client向Binder驱动发起获取服务的请求,并传递要获取哪种服务的名称;
Binder驱动将请求转发给ServiceManager进程;
ServiceManager进程收到请求,并查找是否有Client想要服务的信息;
通过Binder驱动将服务信息传递给Client(此时已经与Sever绑定);
使用服务
Binder驱动为跨进程通信作准备(准备内存映射mmap());
1.BInder创建一块接收缓存区
2.实现内存映射(从Service Manager中获取到Server注册的信息,并找到对应进程。然后将内核缓存区和Server的用户空间地址同时映射到同一块接收缓存区)
Client将数据发给Server;
1.Client通过系统调用copy_from_user()发送数据到内核缓存区(发之后睡眠)
由于建立了映射关系,此时等同于直接将数据发送到了Server的用户空间
2.Binder通知Server执行解包
Server根据Client需求,调用Client方法;
1.收到Binder通知后,Server从线程池中取出线程,进行数据包解包并调用目标方法
2.并将执行结果写到直接用户空间的共享内存中
Server将目标方法结果返回Client;
由于映射关系,Server写在用户空间的结果直接映射到内核缓存空间
1.Binder通知Client获取结果(唤醒)
2.Client通过系统调用copy_to_user()获取内核缓冲区的数据