Binder原理

在阅读本篇文章之前,建议先阅读一下这篇文章

一、概述

本篇博文的目的是学习整理,以备忘。

二、Binder的优势

      Andriod系统是基于Linux内核的,Linux已经提供了管道、消息队列、Socket等IPC机制。为啥Android还要自己搞Binder来实现IPC的。

共享内存虽然不需要数据拷贝,但是控制复杂。

Socket作为一款通用接口,其传输效率低,开销大,主要用于跨网络的进程间通信和本机上进程间的低速通信。

消息队列和管道采用存储-转发的方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。

总结(性能、稳定性、安全性):

三、传统的IPC机制原理

 

这种传统的 IPC 通信方式有两个问题:

  1. 性能低下,一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝;

  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

非常感谢。

喜欢深究的还可以参阅:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值