当前位置: 首页 > news >正文

Android中的Binder

binder是Android平台的一种跨进程通信(IPC)机制,从应用层角度来说,binder是客户端和服务端进行通信的媒介。

ipc原理

ipc通信指的是两个进程之间交换数据,如图中的client进程和server进程。

Android为每个进程提供了虚拟内存空间,而每个Android进程只能运行在自己进程所拥有的虚拟内存空间。

内存空间又分为用户空间和内核空间,前者的数据不能进程间共享,但后者可以。图中的Client进程和Server进程就是利用了进程间可以共享各自内核空间的数据,来完成底层通信的工作。

Android的C/S通信机制

C/S通信指的就是Client和Server两个进程的通信,但实际通信时除了包含这两个进程,还有一个Service Manager,它用于管理各种服务。

这些服务通常是Android系统的核心功能模块,例如传感器管理、电源管理、WIFI管理、闹钟服务等等,与Android四大组件中的服务不同。

当一个Server(服务端)想要提供一种服务,首先需要在Service Manager注册该服务;

而当Client(客户端)想要使用Server中的服务时,不能直接访问,而是要从Service Manager获取该服务,才能使用Server所提供的服务,来与Server进行通信。

Binder通信模型

在引入binder机制后,客户端、服务端和Service Manager之间不能通过api直接互相访问,而是与内核空间的binder驱动通过ioctl方式来完成进程间的数据交换。

关键概念

  • Binder实体对象:Binder服务的提供者,类型是BBinder,位于服务端
  • Binder引用对象:Binder实体对象在客户端进程的代表,类型是BpBinder,位于客户端
  • IBinder对象:Binder实体对象和引用对象的统称,也是他们的父类
  • Binder代理对象:又称接口对象,为客户端的上层应用提供接口服务,类型是IInterface

Binder引用对象和代理对象都是服务端进程中的,把它们分离的好处是一个代理对象可以有多个引用对象,方便上层应用使用。

通信过程

注册服务

  1. server进程向binder驱动申请创建服务的binder实体 
  2. binder驱动为这个服务创建位于内核的binder实体和binder引用
  3. 创建完成后,服务端通过binder驱动将binder引用发送给service manager
  4. service manager收到数据后,取出被创建服务的名字和引用,填入一张查找表

通过以上步骤,server进程通过binder驱动完成了在service manager的服务注册。

在注册服务的过程中,server进程是客户端,而service manager是服务端。

获取服务

  1. Client进程利用handle值为0的引用找到service manager
  2. Client进程向service manager发送xxxservice的访问申请
  3. service manager从请求表中获取xxxservice的名字,在查找表中找到对应的条目,取出对应的binder引用
  4. service manager把xxxservice的binder引用传给Client进程

使用服务

在使用服务时,Client和Server进程都是发送方和接收方。 

这是因为Client在发送服务请求时,Server是接收方;当Server返回数据给Client时,Client变成了接收方。

不论发送方是谁,都会通过自身的Binder实体,把数据发送给接收方的Binder引用。binder驱动回处理发送请求,利用内核空间进程共享机制如下:

  1. 把发送方的数据存入写缓存(binder_write_read.write_buffer)(对于接收方这是读缓存)
  2. 接收方一直处于阻塞状态,当写缓存有数据,会读取数据执行命令操作
  3. 接收方执行操作后,会把结果返回,同样放在写缓存区(对于发送方这是读缓存)

Android中的Binder

 activity、service等组件都需要与ams(system_server)通信,这种跨进程的通信是由binder完成的。从不同角度分析binder如下:

  • 机制:binder是一种进程间通信机制
  • 驱动:binder是一个虚拟物理设备驱动
  • 应用层:binder是一个能发起通信的java类:在java中,如果想要进程通信,就要继承binder

为什么要使用多进程进行开发?

虚拟机分配给各个进程的运行内存是有限制的,lmk也会优先回收占用系统资源大的进程。

对于多进程开发的优势一般有以下几点:

  • 突破进程内存限制,为占用内存大的单独开辟一个进程
  • 功能稳定性:如为通信线程保持长连接的稳定性
  • 防止内存泄漏:如为容易内存泄漏的webview单独开辟一个进程
  • 隔离风险:对于不稳定的进程放在独立进程,避免主进程崩溃

Binder有什么优势 

首先回顾linux进程间的通信机制:管道、信号量、共享内存、socket。

从性能出发,共享内存 > binder > 其他ipc。

但共享内存的缺点也十分明显。与线程之间共享同一块内存相同,共享内存的进程也很容易出现死锁、数据不同步等问题,操作不方便。同时,socket作为一款通用接口,开销过大。

最重要的一点是安全性。传统ipc模式普遍存在的问题是依赖上层协议和访问接入点是开放的。

以创建服务为例,系统需要知道创建人的身份,但在传统ipc机制中,这个身份的获取是从上层协议获取的,即app将id传给系统,而app传回的内容可以是不真实的。对比之下,服务在被创建时,binder就会为创建人分配唯一的uid(用户身份)。

第二点以服务器为例,如果ip是开放的,服务器很容易就会被攻击。同样的,对于传统ipc,如果接入点被知晓,所有人都可以访问。对比之下,binder同时支持实名和匿名。实名与传统ipc相同,是开放的;匿名指的是如果有人需要获取服务,需要先获取到binder内部的引用,才能进行访问。通常的,系统服务是实名的,个人服务是匿名的。直接在service manager中注册的服务是实名的。

Binder是如何做到一次拷贝的

在回答问题之前详细解释一下ipc和虚拟内存的概念。

进程间通信和线程间不同的原因是两者的内存机制不同。对于线程而言,它们的内存是共享的,但进程之间的内存是相互隔离的。

在ipc原理中我们看到进程内部分为用户空间和内核空间,出于安全两者之间是隔离的,app和系统分别处理用户空间和内核空间。

设想如果进程中没有对这两部份进行隔离,app就可以任意访问系统才能访问的数据,而正常来说这样的访问是需要权限的。但这不意味着两者之间完全隔离。系统为两者通信提供了api(copy_from_user & copy_to_user),使得两者间可以互相通信。

对于我们编程而言,需要系统分配虚拟内存,这是因为物理内存不一定是一整块内存,而这种整块内存恰恰是我们在编程中常常需要的。

虚拟内存是通过MMU内存管理单元来映射到物理内存的。在设计的时候,所有进程的内核空间都被映射到了同一块物理内存。这么做的好处就是实现了内存共享,一个进程可以很方便的去获取物理空间中其他进程的内核空间。

 以上就是传统的ipc通信方式。一个进程把自身用户空间的数据通过copy_from_user拷贝到内核空间,而另一个进程通过copy_to_user第二次拷贝从内核空间获取数据到自己的用户空间,这就拷贝了两次。

在binder机制下通信时,内核空间和数据接收方的用户空间映射了同一块物理内存。

简而言之,获取数据的进程的用户空间和内核空间都分配了一小块内存空间,指向同一块物理内存。而这就意味着当被获取数据的进程,从用户空间复制数据到内核空间后,如果内核空间把这个数据存储到这一小块内存空间,另一个进程就能够直接获取,不需要再做一次复制。

MMAP的原理

Linux将一个虚拟内存区域,与磁盘上的物理内存区域关联起来,以这种方式初始化这个虚拟内存区域的内容。这个过程称为内存映射(memory mapping)。

用户空间是不能直接访问磁盘上的内容的,如需访问要通过内核空间,这是肯定很慢的。 首先需要调用write方法从用户空间复制到内核空间,再把数据复制到磁盘,完成写入。

因此当我们使用mmap时关联了虚拟内存和物理内存,当我们在虚拟内存做操作时,物理内存就会直接被修改。

 

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Android】安卓打开指定厂商的应用市场
  • 打开一个页面,整个过程会使用哪些协议?
  • UE基础 —— 介绍与安装
  • Python爬虫入门实战(详细步骤)
  • 【mDNS协议】通过UDP广播在局域网内实现设备自动发现和连接的协议
  • Android 应用兼容性变更调试
  • 【C语言知识-输出空格】C语言中输出空格的方法
  • C# ?的使用
  • 目标检测——X光安检数据集
  • 快速上手的企业视频会议系统需要具备哪些能力
  • 魔众文库系统v7.0.0版本推荐店铺功能,管理菜单逻辑优化
  • 【leetcode刷题之路】面试经典hot100(1)——哈希+双指针+滑动窗口+子串
  • HTMLCSS
  • 面试官:怎样设计一个分布式任务调度平台?
  • 【开源分享】PHP在线提交工单源码|工单管理系统源码 (附源码搭建教程)
  • [译]如何构建服务器端web组件,为何要构建?
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • 2017 年终总结 —— 在路上
  • angular学习第一篇-----环境搭建
  • java8 Stream Pipelines 浅析
  • MySQL Access denied for user 'root'@'localhost' 解决方法
  • nodejs调试方法
  • React-redux的原理以及使用
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 纯 javascript 半自动式下滑一定高度,导航栏固定
  • 从输入URL到页面加载发生了什么
  • 那些被忽略的 JavaScript 数组方法细节
  • 手写一个CommonJS打包工具(一)
  • 湖北分布式智能数据采集方法有哪些?
  • 交换综合实验一
  • ​比特币大跌的 2 个原因
  • #pragma data_seg 共享数据区(转)
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第6节 (嵌套的Finally代码块)
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (回溯) LeetCode 78. 子集
  • (免费领源码)Java#ssm#MySQL 创意商城03663-计算机毕业设计项目选题推荐
  • (三十五)大数据实战——Superset可视化平台搭建
  • (四)模仿学习-完成后台管理页面查询
  • (算法)N皇后问题
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • (转)重识new
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .NET Core中的去虚
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET面试题(二)
  • .NET上SQLite的连接
  • .NET设计模式(11):组合模式(Composite Pattern)
  • .net用HTML开发怎么调试,如何使用ASP.NET MVC在调试中查看控制器生成的html?
  • .set 数据导入matlab,设置变量导入选项 - MATLAB setvaropts - MathWorks 中国
  • @Repository 注解
  • @Transactional类内部访问失效原因详解