简介

Android是如何实现跨进程通信的,大家熟悉的Binder是什么,怎么设计的,进程间的数据如何发送接收的。本文将以及解析,并对Binder驱动实现、Native层实现、Java层实现三块做一个总结分析。

Binder学习思路

  1. Binder与传统IPC的区别
  2. Binder驱动的内部设计、数据结构
  3. Binder驱动与应用程序进程(C/S)之间的通信过程
  4. Android应用程序通过Binder驱动进行通信的流程
  5. Android开发人员如何使用Binder通信(AIDL、Java层架构)

基础知识理解

  1. Unix内核和应用程序进程所使用的物理内存是分开的,内核使用1G的物理内存,其他应用程序有各自的3G物理内存(32位操作系统)
  2. 因为内核和应用程序的物理内存是分开的,所以两者之间传递数据需要进行数据拷贝
  3. 内存映射(mmap)可以将两个虚拟内存地址空间(不同进程)映射到同一物理内存段上。实现多进程(或者内核与进程)之间公用一块内存,减少数据拷贝次数
  4. Unix驱动程序是一个运行在内核态(使用内核对应的物理内存)的程序
  5. Binder也是一种IPC的实现方式,其与传统的Unix IPC有一定的差别(使用了mmap)

理解Binder驱动的存在

因为要实现跨进程通信,那么,数据是如何传输的,怎么组织的。两个进程之间是如何知道对方的标识(引用)的,这一系列问题,都由Binder驱动解决,每个进程需要为其他进程提供服务(API调用),都需要向Binder驱动注册,其他进程才能知道自己的数据传向哪里。这里大家先忽略ServiceManager的特殊身份。只讨论Binder驱动的觉得定位即可。

这样看来,其实Binder驱动就是一个多个进程之间的中枢神经,支撑起了Android中进程间通信,它内部的设计,与应用程序进程中的业务,不存在任何耦合关系,只负责实现进程间数据通信。可以用如下图来理解Binder驱动与应用程序进程之间的关系。

当然,Android里的Binder架构应该还有ServiceManager这个系统服务。

ServiceManager的存在

ServiceManager下文简称SM,是一个Android操作系统提供的一个系统进程。那么为什么要单独提他呢,因为这个进程里,记录了所有Binder实体(提供服务的Binder实现对象)的信息。

也就是说,SM是用来给应用程序查找其他应用程序的数据中心与校验中心,保障进程间通信的安全新,合法性。

SM是系统服务,在系统启动后,SM便启动,并执行以下事情:

  1. 打开Binder驱动
  2. 将自己注册为Binder驱动的大管家(其他进程根据引用编号0可以找到SM对应的Binder实体)
  3. 进入循环,不断从Binder驱动中读取消息(无消息被阻塞)
  4. 读取到消息之后处理消息
  5. 不断循环,永不退出

SM处理的消息类型有:

  1. 注册Binder实体对象的
  2. 查询Binder实体对象,以引用编号的形式放回给查询进程

SM的功能比较简单,就是注册和查询,是第一个注册到Binder驱动中的Binder实体,引用编号0(只能注册一次)

注册Binder实体信息到SM的时候,请求数据中需要写到Binder实体的描述信息,之后进行查询的时候就是根据描述信息来获取到对应的Binder应用编号。

到这里,我们可以看出,其实整个Binder架构就是一个Client,Server,DNS的结构,当然Binder驱动就扮演了一个路由器的角色。

这个结构的前提,就是DNS需要提前注册。也就是说SM进程需要第一个注册到Binder驱动中,而且,Client和Server都知道SM的引用编号(0),能够直接通过SM获取其他进程提供的Binder引用编号。

Binder驱动启动过程

打开
  1. 每个需要通过Binder通信的进程都需要打开/dev/binder驱动一次(至多一次)
  2. 打开Binder驱动之后,内核会调用驱动程序的binder_open方法,该方法内部将会创建binder_proc结构体,内存存储了进程信息以及UID信息。
内存映射
  1. 使用mmap对/dev/binder进行内存映射操作
  2. 在mmap调用之后,内核会调用驱动程序的binder_mmap方法,该方法内部会为进程创建binder_buffer结构体,也就是为进程创建缓冲区,用于接收数据。并且这块内存缓冲区对应有两个虚拟内存地址区间,一个是内核的虚拟空间,一个是进程用户空间的虚拟空间。此块缓冲区是一个只读的区域,防止用户空间对其进行修改。
动作执行者

对于应用程序进程来说,打开驱动和内存映射动作由Native类ProcessState完成,该类为单利,在构造方法中进行,先打开,再执行内存映射。

Binder与共享内存之间的区别

为什么与共享内存进行对比(性能),是因为共享内存管是unix中最快的一种IPC机制。

共享内存为什么快,是因为共享内存相当于是将两个进程的虚拟地址空间指向了一块物理内存,两个进程对该内存区域的修改,能够直接反应到对方进程中,也就是不需要对数据进行拷贝

前面说到,Binder是通过mmap来实现的,理论上,mmap也可以让两个进程映射到同一段物理内存区域(文件)上。但是Binder没有这样实现,如果这样的话,和共享内存就一样了。那Binder又是如何实现的呢。

首先,Binder有驱动程序,所有数据传输和接收,都是通过Binder驱动来操作的。这就带来一个问题,Binder驱动是运行在内核态的,那么数据在使用Binder驱动传输时,是需要在内核内存空间用户内存空间进行拷贝操作的。
试想下,A进程与B进程进行通信,A进程给B进程发送数据data,按照上面的分析,数据data需要先从A进程的用户空间拷贝到Binder驱动的内核空间,再通过Binder驱动写入到(具体实现后面说)B进程的Binder驱动内核空间,最后从Binder驱动再拷贝的B进程的用户空间。如此一来,数据进行了两次拷贝。

其实,Binder驱动内部并不需要两次数据的拷贝,原因在于Binder将内核内存空间用户内存空间进行了内存映射操作,具体如下图

首先,我们从数据接收进程看,内核与用户内存空间,通过mmap映射到了同一块物理内存上。也就是说对该块物理内存的修改,将会提现到数据接收进程的用户空间和内核空间。

再看数据发送进程,左边的数据发送进程,只是将内核的内存空间映射到了物理内存上。

接着,当数据发送进程需要向数据接收进程传递数据时,数据只需要从数据发送进程的用户内存空间拷贝到数据发送进程的内核内存空间,此时,因为数据发送进程的内核内存空间与物理内存进行了映射,而数据接收进程的用户内存空间与内核内存空间同时都映射到了同一块物理内存上,所以此次拷贝,直接将数据发送进程的用户空间数据,拷贝到了数据接收进程的用户内存空间。

通过上面的分析,也就能理解,为什么说Binder传输数据时需要拷贝1次数据,共享内存不需要拷贝数据

Binder的实现架构

完成对Binder跨进程通信底层IPC实现分析之后,需要思考,Android如何让两个进程建立联系(如何找到通信进程),那就需要一个系统进程,所有应用程序都知道它,并能联系到它,从这个系统进程那边,能够查找到(通过Service名字符串)需要通讯的进程。

最终,Android采用了ClientServerServiceManager的实现架构,其中Client需要从ServiceManager中找到Server,然后ClientServer之间即可进行通信

那么什么进程能够在ServiceManager中注册呢,就是在Android操作系统中注册过(APP清单文件中的Service)的那部分服务才能注册,到这,也就能理解Android为什么采用这种架构模式了,在安全上又进一步约束。

Binder驱动

首先要知道Binder驱动是运行在内核态下,内核态的内存是所有进程共享的。

  • 任务一:存储所有进程的Binder信息(引用编号,Server端的虚拟内存地址)
  • 任务二:进程间数据传递

Binder是什么

Binder是什么,需要从多方面解释,不同环境中,其代表的是不一样的东西。

Binder在Server中的表述

Binder在Server中代表的是具体的实现,简称Binder实体

Binder在Client中的表述

Binder的具体实现应该是在Server进程,也就是说Client进程是无法拿到该实现对象的地址信息的。
那么Binder在Client中代表的仅仅是一个引用(驱动给的)编号,Client能够通过该编号向远端Server发送数据。

Binder在驱动中的表述

驱动,是Binder架构在最核心的一部分,驱动需要做的事情很多

  • 所有Server端的Binder实体,需要在驱动中注册
  • Client端获取Binder时,需要为Client创建Binder引用,并把引用编号信息记录在驱动中
  • 维护各个Client中的引用于Binder实体之间的映射关系
  • 通过引用编号找到对应实体
  • 创建Server端的Binder实体
  • etc…

Binder在驱动中的表述,需要从Server和Client两个维度理解

Binder实体(Server端)在驱动中的表述

Binder实体需要在驱动中进行注册,注册时,驱动需要在内核中为Binder实体创建一个结构体binder_node
该结构体中存储的主要数据

  • Server端Binder实体对象的内存地址
  • Server端Binder实体在所有实体链表中的节点结构体

说明:每个Server进程都对应有一个链表,用来存储所有的Binder实体节点,以Binder实体对象的内存地址为索引进行查找。

Binder引用(Client端)在驱动中的表述

Binder引用在驱动中以binder_ref结构体的形式存在。改结构体中存储的主要数据为:

  • Binder实体在驱动中的结构体引用
  • Binder实体在驱动中的引用号(编号)
  • Binder引用在进程链表中的节点(以编号以及实体地址为索引的两个链表节点)

说明:每个Client进程都对应有两个链表,一个是以Binder实体在驱动中的结构体地址为索引建立的链表,一个是以Binder实体在驱动中的引用号为索引建立的链表。

Binder在传输数据中的表述

虽然Binder实体和Binder引用都在驱动中有不同的结构体来标识,但是Client和Server在于Binder进行通信时,并不是通过传递这两个结构体来代表不同的Binder的,而是通过另一个统一的结构体flat_binder_object来代表本次通信对应的Binder。

既然使用的是同一个结构体,那么这个结构体中应该有的内容:

  • Binder类型(实体,引用)
  • Binder实体的内存地址(类型为实体时用)
  • Binder引用的编号(类型为引用时用)

其中Binder类型有以下几种:

  • BINDER_TYPE_BINDER:表示传递的是Binder实体,并且指向该实体的引用都是强类型;
  • BINDER_TYPE_WEAK_BINDER:表示传递的是Binder实体,并且指向该实体的引用都是弱类型;
  • BINDER_TYPE_HANDLE:表示传递的是Binder强类型的引用
  • BINDER_TYPE_WEAK_HANDLE:表示传递的是Binder弱类型的引用
  • BINDER_TYPE_FD:表示传递的是文件形式的Binder

那么flat_binder_object里的内容填充方式具体是怎样的呢,比如Server将Binder传递给Client,Server发送的flat_binder_object,类型应该是BINDER_TYPE_BINDER,此时,驱动将会在内核中为Server进程创建对应的binder_node结构,并且将flat_binder_object中的Binder实体的内存地址保存起来。接着驱动需要在内核中为Client进程创建一个binder_ref结构,因为Server传过来的Binder实体的内存地址在Client进程是无效的,所以驱动需要为Client进程创建一个Binder对应的引用编号,并将此编号存入binder_ref结构中。同时,需要将flat_binder_object中的类型改成BINDER_TYPE_HANDLE,以及存储引用编号。

当Client需要使用Server传递过来的Binder的时候,向驱动传递的数据包中,就需要用到Binder的引用编号,驱动将会对引用编号进行校验,这样就能在安全性上得到保障。

Binder表述总结

当一个Server进程创建了一个Binder实体,之后,这个实体在各个环境中的表述情况为

  1. Server进程中的Binder称为Binder实体,其应该要继承BBinder类(Native类)
  2. 其在Binder驱动中,以binder_node表述
  3. 当Server进程的Binder服务需要被Client进程所使用时,Binder驱动会创建一个binder_ref结构体,这也就是Server中创建的Binder实体在Client进程中的表述(存储引用编号)
  4. 在Client的用户空间中,需要创建一个Binder代理类,该类继承BpBinder类,Client进程通过该代理类与Server端的Binder实体进行通信

    (图片摘自:《Android系统源码情景分析》)

它们的交互过程可以划分为五个步骤,如下所示。

  1. 运行在Client进程中的Binder代理对象通过Binder驱动程序向运行在Server进程中的Binder本地对象发出一个进程间通信请求,Binder驱动程序接着就根据Client进程传递过来的Binder代理对象的句柄值来找到对应的Binder引用对象。
  2. Binder驱动程序根据前面找到的Binder引用对象找到对应的Binder实体对象,并且创建一个事务(binder_transaction)来描述该次进程间通信过程。
  3. Binder驱动程序根据前面找到的Binder实体对象来找到运行在Server进程中的Binder本地对象,并且将Client进程传递过来的通信数据发送给它处理。
  4. Binder本地对象处理完成Client进程的通信请求之后,就将通信结果返回给Binder驱动程序,Binder驱动程序接着就找到前面所创建的一个事务。
  5. Binder驱动程序根据前面找到的事务的相关属性来找到发出通信请求的Client进程,并且通知Client进程将通信结果返回给对应的Binder代理对象处理。

.
从这个过程就可以看出,Binder代理对象依赖于Binder引用对象,而Binder引用对象又依赖于Binder实体对象,最后,Binder实体对象又依赖于Binder本地对象。
.
摘自:罗升阳. Android系统源代码情景分析(第三版)


参考连接:
《Android系统源码情景分析》
《深入理解Android内核设计思想》
写给 Android 应用工程师的 Binder 原理剖析
Android Binder设计与实现 - 设计篇

Android Binder实现浅析-Binder驱动相关推荐

  1. Android Binder机制浅析及AIDL的使用

    参考 轻松理解 Android Binder,只需要读这一篇 图文详解 Android Binder跨进程通信的原理 Android中的Parcel是什么 Android Binder IPC通信机制 ...

  2. Android 进阶8:进程通信之 Binder 机制浅析

    读完本文你将了解: IBinder Binder Binder 通信机制 Binder 驱动 Service Manager Binder 机制跨进程通信流程 Binder 机制的优点 总结 Than ...

  3. 写给 Android 应用工程师的 Binder 原理剖析

    2019独角兽企业重金招聘Python工程师标准>>> 一. 前言 这篇文章我酝酿了很久,参考了很多资料,读了很多源码,却依旧不敢下笔.生怕自己理解上还有偏差,对大家造成误解,贻笑大 ...

  4. Android的IPC机制Binder

    第一部分 Binder的组成  1.1 驱动程序部分驱动程序的部分在以下的文件夹中: Java代码  kernel/include/linux/binder.h kernel/drivers/andr ...

  5. Android native进程间通信实例-binder篇之——解决实际问题inputreader内建类清楚缓存...

    我在实际开发中,遇到一个问题,在电容屏驱动中没有发送input_sync 给上层,导致电容屏有的数据缓存在inputreader 中,会导致系统一系列奇怪问题发生, 至于为什么驱动不发送input_s ...

  6. Android Binder机制(1):Binder架构分析

    从这篇博客开始,将进入Binder机制的分析系列,顺序是先讲解Binder机制的框架,理解了整体思想后,再深入分析各层的细节实现,最后会实现一个自己的本地服务. 1.Binder的历史 BeOS是Be ...

  7. Android跨进程通信Binder机制与AIDL实例

    文章目录 进程通信 1.1 进程空间划分 1.2 跨进程通信IPC 1.3 Linux跨进程通信 1.4 Android进程通信 Binder跨进程通信 2.1 Binder简介 2.2 Binder ...

  8. Android进程间通信之一:Binder机制学习

    Binder机制学习 Binder驱动 Binder核心API Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用. Linux 下的传统 IPC 通信原理 Linux 下的传统 ...

  9. Binder Driver浅析:Binder线程池

    线程池机制 大致流程 每个业务模块创建自己的AIDL接口并实现此接口,这个时候不同业务模块之间不能有耦合,所有实现细节单独开来,然后向服务端提供自己的唯一标识和其对应的Binder对象:对于服务端而言 ...

  10. 由浅入深 学习 Android Binder(十一) binder线程池

    Android Binder系列文章: 由浅入深 学习 Android Binder(一)- AIDL 由浅入深 学习 Android Binder(二)- bindService流程 由浅入深 学习 ...

最新文章

  1. 为增进理解力而奋斗终身
  2. linux进程管理机制,linux进程管理,linux进程管理机制
  3. 十大互联网公司都在寻找她!她是什么样的?
  4. sql server insert 锁表_SQL简单优化
  5. Python__封装
  6. 统计数组中重复元素个数
  7. Eclipse如何新建TOMCAT并配置Server Locations和Publishing属性
  8. mac git 命令自动补全
  9. ibm笔记本电脑电池_笔记本电脑是一直插着电源好,还是拔了电源好?
  10. [HDOJ3068]最长回文
  11. c语言中关键字的分类,C语言关键字分类整理
  12. DEVCON.EXE管理USB
  13. PHP开发安全之近墨者浅谈(转)
  14. 理解JPEG图像压缩算法,DCT变换
  15. 光纤中的多种光学模式芯径_「涨知识」你想知道的光纤常识都在这里了,看不看随你...
  16. 53所高校研究生补贴一览表
  17. 报错Content type ‘multipart/form-data;boundary=----WebKitFormBoundaryTz0sivpVO7U0H70m;charset=UTF-8‘ n
  18. springboot导出excel(easyexcel和poi 列下拉及表格锁定)
  19. Javascript实现扫雷游戏
  20. 论PMI-ACP敏捷项目管理认证考前培训必要性

热门文章

  1. 单片机c语言中void key(void),单片机C语言编程
  2. python画图案 使用循环完成_利用python在终端模拟下雪的效果
  3. Cobalt Strike参数详解
  4. 痕迹清理 - Windows
  5. 2020-8-5 Codeforces摸鱼报告
  6. 数据结构与算法python语言描述第三章课后答案_《数据结构与算法Python语言描述》习题第二章第三题(python版)...
  7. date和datetime长度设置多少_太原市玻璃温室大棚多少钱
  8. pageHelper 分页插件使用
  9. Java_基础—List集合存储学生对象并遍历
  10. 【转】plist文件的内容清空