在阅读本篇文章之前,建议先阅读一下这篇文章。
一、概述
本篇博文的目的是学习整理,以备忘。
二、Binder的优势
Andriod系统是基于Linux内核的,Linux已经提供了管道、消息队列、Socket等IPC机制。为啥Android还要自己搞Binder来实现IPC的。
共享内存虽然不需要数据拷贝,但是控制复杂。
Socket作为一款通用接口,其传输效率低,开销大,主要用于跨网络的进程间通信和本机上进程间的低速通信。
消息队列和管道采用存储-转发的方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。
总结(性能、稳定性、安全性):
三、传统的IPC机制原理
这种传统的 IPC 通信方式有两个问题:
-
性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;
-
接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,这两种做法不是浪费空间就是浪费时间。
三、Binder机制的原理
1、动态内核可加载模块
跨进程通信是需要内核空间作为支持的。传统的IPC机制如管道、Socket都是内核的一部分,因此通过内核支持实现进程间通信自然是没问题的。但是Binder并不是Linux系统内核的一部分,那该怎么办呢?幸好,Linux支持动态内核可加载模块的机制,模块是具有独立功能的程序,它可以被单独的编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样Android就可以动态添加一个内核模块运行在内核空间,用户进程通过这个内核模块作为桥梁实现通信。
在Android系统中,这个运行在内核空间,负责各个用户进程通过Binder实现通信的内核模块就是Binder驱动。
区别于传统的IPC通信机制,Binder IPC机制实现的原理是:内存映射。
内存映射能减少数据拷贝的次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反应在映射的内存区域上,从而被对方感知。也正因为如此,内存映射能够提供对进程间通信的支持。
2、原理
Binder IPC是基于内存映射(mmap)实现的,但是mmap()通常是用在物理介质的文件系统上的。
比如进程中的用户区域是不能直接和物理设备打交道的,如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘-->内核空间-->用户空间);通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。
而Binder并不存在物理介质,因此Binder驱动使用mmap()并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。
一次完整的Binder IPC通信过程通常是这样:
a)首先Binder在内核空间创建一个数据接收缓冲区;
b)接着内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核数据接收缓存区和接收进程用户空间地址的映射关系;
c)发送方进程通过系统调用copy_from_user将数据copy到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在映射关系,因此也相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
四、Binder通信模型
Service Manager (相当于DNS服务器)和 Binder 驱动(相当于路由器)由系统提供,而 Client、Server 由应用程序来实现。
Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。
通信过程如下:
a)首先,一个进程使用BINDER_SET_CONTEXT_MGR命令通过Binder驱动将自己注册成ServiceManager;
b)Server通过驱动向ServiceManager中注册Binder(Server中的Binder实体),表明可以对外提供服务。驱动为这个Binder创建位于内核中的实体节点以及ServiceManager对实体的引用,将名字以及新建的引用打包传给ServiceManager,ServiceManager将其填入查找表。
c)Client通过名字在Binder驱动的帮助下从ServiceManager中获取到对Binder实体的引用,通过这个引用就能实现和Server进程的通信。
五、Binder中的代理对象
进程A想要进程B中某个对象是如何实现的?毕竟它们属于不同的进程,A进程没法直接使用B进程中的对象(object);
跨进程通信的过程都有Binder驱动的参与,因此在数据流经Binder驱动的时候,驱动会对数据做一层转换。当A进程想要获取B进程中的object时,驱动并不会真的把object返回给A,而是返回一个跟Object看起来一模一样的代理对象objectProxy。这个代理对象和对象具有一模一样的方法,但是这些方法并没有B进程中object对象那些方法的能力,这些方法只需要把请求参数交给驱动即可。对于A进程来说和调用B进程的object中的方法是一样的。
当Binder驱动接收到A进程的消息后,发现这是一个ObjectProxy就去查询自己维护的表单,一查发现这是B进程object的代理对象。于是就会去通知B进程调用object的方法,并要求B进程返回结果给自己。当驱动拿到B进程的返回结果之后,就会转发给A进程,一次通信完成。
总结:
-
从进程间通信的角度看,Binder 是一种进程间通信的机制;
-
从 Server 进程的角度看,Binder 指的是 Server 中的 Binder 实体对象;
-
从 Client 进程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个远程代理
-
从传输过程的角度看,Binder 是一个可以跨进程传输的对象;Binder 驱动会对这个跨越进程边界的对象对一点点特殊处理,自动完成代理对象和本地对象之间的转换。
整理来源:https://zhuanlan.zhihu.com/p/35519585
非常感谢。
喜欢深究的还可以参阅: