Android Binder实现浅析-Binder驱动

简介

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驱动与应用程序进程之间的关系。

image

当然,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实体对象,以引用编号的形式放回给查询进程

注册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机制。

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

image

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

首先,Binder有驱动程序,所有数据传输和接收,都是通过Binder驱动来操作的。这就带来一个问题,Binder驱动是运行在内核态的,那么数据在使用Binder驱动传输时,是需要在内核内存空间与用户内存空间进行拷贝操作的。

试想下,A进程与B进程进行通信,A进程给B进程发送数据data,按照上面的分析,数据data需要先从A进程的用户空间拷贝到Binder驱动的内核空间,再通过Binder驱动写入到(具体实现后面说)B进程的Binder驱动内核空间,最后从Binder驱动再拷贝的B进程的用户空间。如此一来,数据进行了两次拷贝。

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

image

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

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

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

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

Binder的实现架构

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

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

那么什么进程能够在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架构在最核心的一部分,驱动需要做的事情很多

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

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实体进行通信

image

原文地址:https://www.cnblogs.com/linhaostudy

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml<?xml version="1.0" encoding="utf-8"?><RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具
文章浏览阅读2.1k次。初学Android游戏开发的朋友,往往会显得有些无所适从,他们常常不知道该从何处入手,每当遇到自己无法解决的难题时,又往往会一边羡慕于 iPhone下有诸如Cocos2d-iphone之类的免费游戏引擎可供使用,一边自暴自弃的抱怨Android平台游戏开发难度太高,又连个像样的游 戏引擎也没有,甚至误以为使用Java语言开发游戏是一件费力不讨好且没有出路的事情。事实上,这种想法完全是没有必_有素材的游戏引擎
文章浏览阅读3.2k次,点赞2次,收藏2次。2014年12月从csdn专家福利获得的一本书《Android游戏开发技术实战详解》,尘封了一年多的时间,今天才翻开来看。我认识中的Android,提到Android最先浮现在我脑海中的是那可爱的机器人图标:这个Logo是由Ascender公司设计的,诞生于2010年,其设计灵感源于男女厕所门上的图形符号(真的是灵感无处不在),于是布洛克绘制了一个简单的机器人,它的躯干就像锡罐的形状,头上还有两根_智能手机的特点有哪些?
文章浏览阅读8.1k次,点赞9次,收藏11次。首先,Android是不是真的找工作越来越难呢?这个可能是大家最关心的。这个受大的经济环境以及行业发展前景的影响,同时也和个人因素有关。2016-08-26近期一方面是所在的公司招聘Java开发人员很难招到合适的,投简历的人很少;而另一方面,经常听身边的人说Android、iOS方面找工作不好找,特别是没什么经验的,经验比较少的!说是不好找,但在我家所在的吉林省省会长春,会Unity3D+Maya_android 开发和asp.net哪个好 site:blog.csdn.net
文章浏览阅读6.1k次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;而如果没有手机或没有对应型号的手机,又想做对应型号(屏幕尺寸、Android系统版本)的适配,应该怎么办呢?这时Android模拟器就派上用场了。Android模拟器Android SDK自带一个移动模拟器。它是一个可以运行在你电脑上的_安卓移动开发软件怎样预览
文章浏览阅读8.9k次。Google IO 2017 上宣布,将Kotlin语言作为安卓开发的官方语言。Kotlin由JetBrains公司开发,与Java 100%互通,并具备诸多Java尚不支持的新特性。谷歌称还将与JetBrains公司合作,为Kotlin设立一个非盈利基金会。Kotlin 是一个基于 JVM 的静态类型编程语言,Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JV_kotlin为什么被嫌弃
文章浏览阅读9.6w次,点赞17次,收藏35次。有些情况下,不方便使用断点的方式来调试,而是希望在控制台打印输出日志,使用过Eclipse的同学都知道Java可以使用 System.out.println(""); 来在控制台打印输出日志,但是在android studio中却是不行的,还是有差别的,那应该用什么呢?android.util.Log在调试代码的时候我们需要查看调试信息,那我们就需要用Android Log类。android.ut_andirod.studio 为什么不在控制台打印输出
文章浏览阅读8.2k次,点赞2次,收藏8次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;这里讲一下如何为应用添加一个按钮,并为按钮添加Click单击事件处理程序,显示/隐藏另一个按钮。添加按钮在HelloWorld项目的基础上,打开界面布局文件:activity_main.xml切换到Design(设计)模式;在组件But_activity_main.xml按钮隐藏
文章浏览阅读2.9k次,点赞3次,收藏9次。android 开发工具主流的还是Android Studio,当然也有很多人喜欢用Eclipse,也有人喜欢用IntelliJ IDEA ;还有Xamarin这种只需要编写一次代码,可以编译多种平台可运行的强大工具。但是它又真的强大吗?就我看来没有,身边很多人还是在用Android Studio、XCode开发应用,没见谁在用Xamarin之类的工具。系统要求WindowsMicrosoft®_android开发下载安装
文章浏览阅读4.2k次,点赞7次,收藏26次。你知道Hello World程序的由来吗?对于大多数编程语言的学习来说,真正入门的一课就是 Hello World!会而不难,难而不会。虽然很多人写过关于Android开发Hello World的文章,但随着时间的推移,开发工具、技术的进步,可能有些已经过时了。我就记录一下当下我所经历的第一个Android APP HelloWorld。一、准备1、开发环境参考:Android Studio 下载_android helloworld textview 句柄获取
这篇“android轻量级无侵入式管理数据库自动升级组件怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定...
今天小编给大家分享一下Android实现自定义圆形进度条的常用方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...
这篇文章主要讲解了“Android如何解决字符对齐问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android...