Binder解析精炼

news2024/9/21 4:38:37

Binder原理解析精炼

1 注册服务

  1. Server进程向binder驱动向Binder驱动发起服务注册请求

    向Binder驱动申请创建一个XXXService的Binder的实体,Binder驱动为这个XXXService创建位于内核中的Binder实体节点以及Binder的引用

  2. Binder驱动将注册请求转发给ServiceManager进程

    Binder驱动将名字和新建的引用打包传递给SM(实体没有传给SM),通知SM注册一个名叫XXX的Service

  3. ServiceManager进程添加该server进程:从Binder驱动里取出handle和name,并构造结构体插入到链表。

此时,servicemanager进程拥有了Server进程的信息

在这里插入图片描述

2 获取服务

  1. client向Binder驱动发起获取服务的请求,传递要获取服务的名称

  2. Binder驱动将该请求转发给ServiceManager进程

  3. ServiceManager查找到client需要的server对应的服务信息:从Binder驱动里取出name,并找到链表里相同的name,找到后取出handle,最后写入到Binder驱动

  4. 通过Binder驱动将上述服务信息返回给client进程

    client进程与server进程已经建立联系

    此时,如果有Client向SM发送申请服务XXXService的请求,那么SM就可以在查找表中找到该Service的Binder引用,并把Binder引用(XXXBpBinder)返回给Client。

3 使用服务

  1. client端调用getService通过handler构造client端的BpBinder(native层),与此对应的是Java层的BinderProxy;

  2. 将ServiceManager的handler与请求服务名写入binder驱动,binder驱动通过SM拿到对应server的handler,从而找到Server进程,进而调用BBinder(Native层),与此对应的是Java层的Binder;

    对应server的handler就是该server的Binder引用

  3. 将拿到的server的handler转换为BpBinder,然后转为java层的BinderProxy

  4. client接口调用:调用BinderProxy.transact(),转为native层的BpBinder.transact(),然后转为handler,将数据写入binder驱动

  5. Server端读取Binder驱动数据,找到BBinder对象,然后转为Binder对象,调用Binder.onTransact()完成对应接口调用,并通过Binder驱动返回处理结果给客户端的服务器代理对象

  6. 客户端收到服务端的返回结果

4.完整流程

在这里插入图片描述
在这里插入图片描述

5 常见问题

1 Binder的数据结构

Binder在传输数据中的表述:flat_binder_object
Binder对象类型
Binder实体在驱动中的表述:binder_node
Binder引用在驱动中的表述:binder_ref
Binder 进程、线程结构:binder_proc和binder_thread
Binder收发数据包结构:binder_transaction_data
Binder写操作命令字: BC_XXX
Binder读操作命令字: BR_XXX

2 一次拷贝

将内核缓存区和接收进程用户空间地址映射到一个接收缓存区

  • 首先 Binder 驱动在内核空间创建一个数据接收缓存区

  • 在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系

  • 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便实现了一次拷贝。

在这里插入图片描述

3 既然使用内存映射技术可以减少数据拷贝次数,为什么发送方进程不做内存映射?

发送方进程也做内存映射确实可以实现数据 0 拷贝传输,这就属于是以共享内存的方式进行 IPC 了。但是从以下两个角度来看,还是使用 Binder 的方式比较适合 Android

  1. 从性能角度来看: Binder 的数据只要拷贝一次,性能仅次于共享内存,且 RPC 调用的数据通常较小,因此拷贝操作对性能的影响并不大。
  2. 从稳定性和复杂性角度来看:Binder是基于C/S架构的,简单解释下C/S架构,是指客户端(Client)和服务端(Server)组成的架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好;而共享内存实现方式复杂,没有客户与服务端之别, 需要充分考虑到访问临界资源的并发同步问题,否则可能会出现死锁等问题

4 oneway 机制

  • 非 oneway 的 aidl 接口,调用方线程会被阻塞,直到被调用方进程返回为止
  • oneway 的 aidl 接口,不会阻塞调用方线程,而是立即返回。在Android Framework 中 AMS 启动 Activity 时就是使用的 oneway 的方式,不会因为应用进程而阻塞了AMS进程的线程
  • oneway 的还有一个特点,它是串行化的,binder 驱动内部有一个队列,会将它一个一个发送给接收进程

那么问题来了,oneway 的接口立即返回,怎么拿到被调用方进程的处理结果呢?

首先,如果我们不需要关注返回结果的,建议使用 oneway,如果需要返回结果,也可以用 oneway,有两种方式:

  1. 注册监听,将 callback 也定义成 aidl 接口,然后通过注册方式向接收方进程传递,接收方进程拿到 callback,通过 binder 机制可以向发送方回调数据
  2. 直接在定义方法的时候,传入 aidl 定义的 callback 接口,同上机制

但是要注意,通过单独注册监听的方式,如果需要反注册的话,接受方进程维护的监听列表,要使用 RemoteCallbackList 来存储。因为发送方的 callback 和接收方拿到的 callback 不是同一个对象,而 RemoteCallbackList 内部通过以 callback 对应的 binder 作为 key (虽然不是同一个对象,但是 binder 是同一个),所以可以保证注册和反注册正常,同时 RemoteCallbackList 内部做了线程安全保障(注册和反注册都是synchronized 方法),不需要自己额外处理多线程问题

5 Binder 通信的数据大小限制

在进程创建的时候会为 Binder 创建一个1M -8k的缓冲区用于跨进程通信时的数据传输

Binder 调用中同步调用优先级大于 oneway(异步)的调用

为了充分满足同步调用的内存需要,所以将 oneway 调用的内存限制到申请内存上限的一半

如果超过1M-8k这个上限就就会抛出这个异常,而且这个缓存区时当前进程内的所有线程共享的,线程最大数量为16个,如同时间内总传输大小超过了1M,也会抛异常。

另外在Activity启动的场景比较特殊,因为Binder 的通信方式为两种,一种是异步通信,一种是同步通信,异步通信时数据缓冲区大小被设置为了原本的1半

6 为什么要用Binder

简单的回答:性能:相比传统的Socket更高效;安全:安全性高,支持通信双方进行身份验证

7 Binder为啥用Parcelable做序列化

Serializable使用简单,但是开销很大(大量I/O操作),Parcelable是Android中的序列化方式,使用起来麻烦,但是效率很高,是Android推荐的方式。Parcelable主要用在内存序列化上,如果要将对象序列化到存储设备或者通过网络传输也是可以的,但是会比较复杂,这两种情况建议使用Serializable。

序列化

序列化是指将一个对象转化为二进制或者是某种格式的字节流,将其转换为易于保存或网络传输的格式的过程,反序列化则是将这些字节重建为一个对象的过程。Serializable和Parcelable接口可以完成对象的序列化。

1)Serializable

Serializable是Java提供的序列化接口,使用时只需要实现Serializable接口并声明一个serialVersionUID(用于反序列化)

2)Parcelable

A. writeToParcel:将对象序列化为一个Parcel对象,将类的数据写入外部提供的Parcel中
B. describeContents:内容接口描述,默认返回0
C. 实例化静态内部对象CREATOR实现接口Parcelable.Creator,需创建一个数组(newArray(int size)) 供外部类反序列化本类数组使用;createFromParcel创建对象
ribeContents:内容接口描述,默认返回0
C. 实例化静态内部对象CREATOR实现接口Parcelable.Creator,需创建一个数组(newArray(int size)) 供外部类反序列化本类数组使用;createFromParcel创建对象
D. readFromParcel:从流里读取对象,写的顺序和读的顺序必须一致。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1925706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Manim学习笔记05:实现向量的加法动画

以同一点 O𝑂 为起点的两个已知向量 →a𝑎→, →b𝑏→,以 OA𝑂𝐴,OB𝑂𝐵 为邻边作 □OACB◻𝑂𝐴𝐶𝐵&#xff…

Yolov8 姿态估计

原文:Yolov8 姿态估计 - 知乎 (zhihu.com) YOLOv8论文还没有,官方默默又加了新模型:姿态估计。 现在你可以用YOLOv8做目标检测、实例分割、图像分类、目标跟踪、姿态估计,未完待续。。。。。。 一、Yolov8姿态估计 Yolov8的姿态估计模型是在COCO数据集训练的,目前支持…

python+Selenium自动化之免登录(cookie及token)

目录 cookie免登录 通过接口获取cookie 启用浏览器绕过登录 添加token 使用登录可以减去每次登录的重复操作,直接操作系统登录后的菜单页面,也可以减少安全验证登录,如图像验证登录的操作。注意:cookie和token都有有效期。 c…

前端Vue组件化实践:打造自定义等宽tabs标签组件

在前端开发的世界里,随着业务复杂度的提升和需求的多样化,传统的整体式开发方式已经难以满足快速迭代和高效维护的需求。组件化开发作为一种重要的解决方案,正逐渐受到广大开发者的青睐。本文将结合Vue框架,探讨如何通过组件化开发…

如何在Linux上如何配置虚拟主机

在Linux上配置虚拟主机可以通过使用Apache HTTP服务器来实现。Apache是一个开源的跨平台的Web服务器软件,可以在多种操作系统上运行并支持虚拟主机的配置。 以下是在Linux上配置虚拟主机的步骤: 安装Apache HTTP服务器 在终端中运行以下命令来安装Apache…

CANoe:为什么两个VLAN接口不能设置同一个网络的IP地址呢?

经常玩CANoe的人应该配置过TCP/IP Stack中网络节点的网卡信息,基本的信息包含:MAC地址、IP地址、子网掩码、默认网关、MTU值、IPv6地址。 如果你想让发送出去的报文携带VLAN tag,可以在网卡上添加VLAN tag信息。 此时你就能得到两个新的网卡V…

加速数字化转型,信创自主可控:TapData 为银行业数据管理能力建设提供新思路

使用 TapData,化繁为简,摆脱手动搭建、维护数据管道的诸多烦扰,轻量代替 OGG、DSG 等同步工具,「CDC 流处理 数据集成」组合拳,加速仓内数据流转,帮助企业将真正具有业务价值的数据作用到实处,…

防火墙nat与智能选路

这里写目录标题 此实验是基于上个实验的基础上添加功能拓扑1办公区设备可以通过电信链路和移动链路上网(多对多的NAT,并且需要保留一个公网IP不能用来转换)首先在fw1防火墙创建电信和移动两个安全区域,并且将对应的接口划分进去配置nat测试 分公司设备可…

Java核心篇之JVM探秘:内存模型与管理初探

系列文章目录 第一章 Java核心篇之JVM探秘:内存模型与管理初探 第二章 Java核心篇之JVM探秘:对象创建与内存分配机制 第三章 Java核心篇之JVM探秘:垃圾回收算法与垃圾收集器 第四章 Java核心篇之JVM调优实战:Arthas工具使用及…

[web]-sql注入-白云搜索引擎

ctrlu查看源代码&#xff0c;发现前端有js过滤 <script>function myFunction(){var xdocument.getElementById("number").value;var adocument.getElementById("word").value;var ba.replace(/[\ |\~|\|\!|\|\#|\$|\%|\^|\&|\*|\(|\)|\-|\_|\|\…

linux之find指令基础

目录 前言一、find .二、find xxx -name "*.c"三、组合查找文件名四、find . -type f五、find . -maxdepth 2 -type f六、find . -type f -perm 777七、find . -type f -name "*.txt" ! -perm 777八、借助-exec命令参考链接 前言 testfind下 check1.c ch…

【HTML入门】第十二课 - iframe框架

在早期没有出现Vue和React之前呢&#xff0c;做管理系统&#xff0c;iframe是非常普遍的技术。比如管理系统左侧有非常多的菜单&#xff0c;然后点击菜单后&#xff0c;右边就要展现不同的页面。 又或者呢&#xff0c;我们看一些网站&#xff0c;他们侧边展示着五彩绚烂的广告&…

在 PostgreSQL 里如何实现数据的实时监控和性能瓶颈的快速定位?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 里如何实现数据的实时监控和性能瓶颈的快速定位一、数据实时监控的重要性二、PostgreSQ…

MySQL学习记录 —— 이십 常用工具包

文章目录 1、总览2、mysqlcheck - 表维护程序1、作用2、注意事项3、语法4、命令选项下面每块都大致有这四个部分 3、Mysqldump - 数据库备份程序4、mysqladmin - MySQL 服务器管理程序5、mysqlshow - 显示数据库、表和列信息6、mysqldumpslow - 总结慢查询日志文件7、mysqlbinl…

福利:领取生育津贴汇总

大家注意了&#xff0c;最近多地区发文&#xff0c;生育津贴有了新变化。为了国家的未来&#xff0c;各位大佬记得全力以赴三胎。 01北京--不用缴费也能领取生育津贴 7月1日&#xff0c;北京市人社局、医保局、财政局、税务局等多部门联合印发了《关于领取失业保险金人员参加生…

【并发编程】进程 线程 协程

进程&#xff08;Process&#xff09;、线程&#xff08;Thread&#xff09;和协程&#xff08;Coroutine&#xff09;构成了计算机科学中实现任务并发执行的三种核心抽象机制。通常&#xff0c;为了提高程序的执行效率&#xff0c;开发者会根据应用场景和性能需求&#xff0c;…

Java核心篇之JVM调优实战:Arthas工具使用及GC日志分析

系列文章目录 第一章 Java核心篇之JVM探秘&#xff1a;内存模型与管理初探 第二章 Java核心篇之JVM探秘&#xff1a;对象创建与内存分配机制 第三章 Java核心篇之JVM探秘&#xff1a;垃圾回收算法与垃圾收集器 第四章 Java核心篇之JVM调优实战&#xff1a;Arthas工具使用及…

什么? CSS 将支持 if() 函数了?

CSS Working Group 简称 CSSWG, 在近期的会议中决定将 if() 添加到 CSS Values Module Level 5 中。 详情可见&#xff1a;css-meeting-bot 、[css-values] if() function 当我看到这个消息的时候&#xff0c;心中直呼这很逆天了&#xff0c;我们知道像 less 这些 css 这些预…

【深度学习】PyTorch深度学习笔记02-线性模型

1. 监督学习 2. 数据集的划分 3. 平均平方误差MSE 4. 线性模型Linear Model - y x * w 用穷举法确定线性模型的参数 import numpy as np import matplotlib.pyplot as pltx_data [1.0, 2.0, 3.0] y_data [2.0, 4.0, 6.0]def forward(x):return x * wdef loss(x, y):y_pred…

【原创】springboot+mysql图书共享交流平台设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…