一个Binder的前生今世 (一):Service的创建

一个Binder的前生今世 (一):Service的创建

  • 一个Binder的前生今世
    • Binder的历史 (字面意义的前生今世)
    • Binder的生命周期(抽象意义的前生今世)
    • Binder 应用及系统层关系图
    • Binder应用层的架构设计
    • Binder应用层实现
    • Binder的创建
      • 服务端Binder的创建
      • 服务端Binder的传递
      • Binder在客户端的传递
      • 客户端BinderProxy的创建
    • 小结

一个Binder的前生今世

目前介绍binder的文章很多,但是大部分都是分层来介绍的,从驱动再到Service manager等,这些文章对研究binder的机制给了很多的帮助和教学,但是这些大部分是从系统的角度去阐述binder机制的,作为应用开发者,希望能有一个从应用开发角度去理解 binder 和其机制的介绍文章,比如从binder的生命周期角度去了解等,遂产生了这篇文章异或笔记

Binder的历史 (字面意义的前生今世)

binder 的前身是 OpenBinder,它是一种基于对象的分布式组件框架,最早由 BeOS 公司开发,后来被 Palm 公司收购,并用于 Palm OS Cobalt 系统。OpenBinder 采用了一种类似于 COM 或 CORBA 的模型,将跨进程通信抽象为对象之间的方法调用,提供了一套完整的接口和协议来实现对象的创建、引用、继承、代理等功能。

OpenBinder 在 Palm OS Cobalt 系统中并没有得到广泛的应用,而是被 Google 公司收购,并用于 Android 系统中。Google 公司对 OpenBinder 进行了大量的修改和优化,使其更适合移动设备的特点和需求。主要的改变有以下几点:

提供了 Java 语言的绑定。
将 OpenBinder 的对象模型简化为引用计数模型,并去掉了继承、代理等复杂的功能。
将 OpenBinder 的通信协议简化为四种基本类型:数据、命令、句柄和文件描述符,并使用 Parcel 类来打包和解包数据。
将 OpenBinder 的驱动程序从用户空间移动到内核空间,并使用 mmap 和 ioctl 来进行内存映射和控制。
经过这些改变后,OpenBinder 就变成了我们现在所熟知的 binder,它成为了 Android 系统中最重要的跨进程通信机制之一。

Binder的生命周期(抽象意义的前生今世)

binder 的生命周期是指一个 binder 对象从创建到销毁的过程,它涉及到多个进程和线程之间的交互和协作。binder 的生命周期主要包括以下几个阶段:

创建:一个进程或线程可以通过继承 BBinder 类或实现 IBinder 接口来创建一个本地端 binder 对象,并通过注册到 service manager 或写入到 Parcel 中来将其传递给其他进程或线程。
获取:一个进程或线程可以通过查询 service manager 或读取 Parcel 中来获取一个远端 binder 对象的句柄,并通过继承 BpBinder 类或使用 Proxy 类来与之通信。
调用:一个进程或线程可以通过调用 transact 方法来向一个远端 binder 对象发送数据和命令,并等待其返回结果。
响应:一个进程或线程可以通过重写 onTransact 方法来接收并处理来自一个远端 binder 对象的数据和命令,并返回结果。
销毁:一个进程或线程可以通过调用 unlinkToDeath 方法来取消对一个远端 binder 对象的引用,并释放其资源。当一个远端 binder 对象没有任何引用时,它就会被销毁。

在本系列博客中,我会介绍每个阶段的具体实现和源码分析,以及一些相关的概念,例如:IBinder, Interface, BBinder, BpBinder, Parcel、ProcessState、IPCThreadState 等以及它们的关系。希望您能够通过这系列博客,对 binder 的前生今世和生命周期有一个具体的了解。

Binder 应用及系统层关系图

先来上一张图:
在这里插入图片描述
上图描述了Binder在应用层的架构设计,和与C++层的具体的Binder类的关系以及与系统层(RuntimeLayer, 也可以叫AppLayer,这两个类是在一个application中共享的实例)Binder机制的关系。

Binder应用层的架构设计

先来总体介绍下: binder的总体架构主要就是C/S 架构。那么binder的C/S架构是如何设计的呢?我们接着看

Binder应用层架构设计主要采用了Proxy模式,主要分为三部分:

  1. IBinder接口,主要代表了binder实体的抽象,打个比方就类似于通话人员的对讲机,通讯双方(客户端,服务端)都需要通过这个binder实体来实现通讯。
  2. IInterface接口,主要是提供给定义具体服务接口的IMyService接口来继承的,通过它可以获取binder,也就是拿到对讲机。
  3. IMyService接口, 是一个Proxy模式的实现,具体代表的通讯双方预定好的可以具体提供哪些服务,可以理解为对讲机两端人员的暗号约定,只有约定的暗号,两端的人员才能识别,不然无法识别。它在客户端的代表就是MyService.stub.proxy, 在服务端的代表就是MyService.stub.

当IMyService定义服务接口时,要继承IInterface以便具有可以获取binder实例的能力。

Binder应用层实现

好了,理解了上面的binder架构设计,我们接着来分析binder具体是如何实现上诉架构设计的。

Binder是IBinder在服务端的实现,它实现了onTranscation接口,用来接受客户端发过来的请求, 然后把请求转发给MyService.stub, MyServic.stub就是我们自己实现的处理服务。通常我们在服务端的实现为:

class MyService() : MyService.Stub() 

BinderProxy是IBinder在客户端的表示,它被MyService.stub.proxy持有,它实现了IBinder的transact函数,用来转发客户端MyService.stub.proxy的函数调用请求。通常我们在客服端的使用方式为:

val myService = MyService.Stub.asInterface(service);

以上就是Binder在Java应用层的基本实现和原理,就是这么简单,通则不难,理解了这些就可以在我们的应用中使用binder了。

我们了解了在应用开发中binder是怎么使用的,那么binder的底层的机制是怎样的? 为什么我们要这样用,为什么我们这样用了就可以IPC通讯了呢?

好,接下来我们就来深入分析binder底层的原理。

首先我们先来具体看下一个binder是如何创建出来的。

Binder的创建

服务端Binder的创建

先上图:
在这里插入图片描述

我们在应用层创建binder的方式一般是在服务端bind的时候,此时,我们会返回一个MyService.stub对象,然后转换成IBinder返回给binder调用。那当我们new一个MyService.stub的时候,系统是如何操作的呢,可以从上图一探流程。

附上各个类的路径:

Binder.java : Android/frameworks/base/core/java/android/os/Binder.javaandroid_util_Binder : Android/frameworks/base/core/jni/android_util_Binder.cppJavaBBinderHolder : Android/frameworks/base/core/jni/android_util_Binder.cppParcel : Android/frameworks/native/libs/binder/Parcel.cppandroid_os_Parcel : Android/frameworks/base/core/jni/android_os_Parcel.cppJavaBBinder: Android/frameworks/base/core/jni/Android_util_Binder.cppBBinder: Android/frameworks/native/include/IBinder.h & Android/frameworks/native/libs/binder/Binder.cppBpBinder : Android/frameworks/native/libs/binder/BpBinder.cpp

总结: 服务端创建一个service.stub对象,这个对象继承Java层Binder,Java层的Binder在C++层持有一个JavBBinderHolder。
然后在Parcel传递的时候,会把Java层的Binder写入Parcel,写入的时候就会调用ibinderForjavaObject来生成JavaBBinder对象,JavaBBinder对象保存对Java层Binder对象的引用在mObject字段中,JavaBBiner对象继承BBinder,进而生成了BBinder对象,BBinder此时就被创建出来了,这个BBinder就是最根本的Binder对讲机的服务端了。

服务端Binder的传递

我们应用层一般时候binder都是通过Parcel传递的。我们接下来就来看看,我们服务端生成了一个Service的binder后,是如何通过Parcel传递出去的。

首次第一步肯定是先把binder写入Parcel ,我们先来分析Java层,首先肯定是调用Java层的Parcel 写入binder,进而就会调用到Parcel的Jni层执行写入操作:

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));if (err != NO_ERROR) {signalExceptionForError(env, clazz, err);}}
}

然后我们再看:

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{if (obj == NULL) return NULL;// Instance of Binder?if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {JavaBBinderHolder* jbh = (JavaBBinderHolder*)env->GetLongField(obj, gBinderOffsets.mObject);return jbh->get(env, obj);}// Instance of BinderProxy?if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {return getBPNativeData(env, obj)->mObject;}ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);return NULL;
}

到这里就会调用到JavaBBinderHolder的get函数,对应到我们服务端Binder的创建流程,这里就会生成JavaBBinder了:

class JavaBBinderHolder
{
public

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实现按钮悬停动画

知识点与技巧 伪元素 使用伪元素来作为按钮悬停效果动画展示的元素 z-index 的使用技巧 使用z-index属性来控制按钮和伪元素的层次关系 transform、transition 复习 使用transform、transition两个属性来实现动画的展示 按钮边框动画 切换效果 核心代码 .btn.btn-border-…

2023面试知识点一

1、新生代和老年代的比例 默认的&#xff0c;新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 )&#xff0c;即&#xff1a;新生代 ( Young ) 1/3 的堆空间大小。老年代 ( Old ) 2/3 的堆空间大小。其中&#xff0c;新生代 ( …

Unity减少发布打包文件的体积——获取精灵图片的信息限制它的大小

一、起因 一个工程&#xff0c;打包成webGL且压缩成zip文件后&#xff0c;接近400M&#xff0c;后来把大的精灵图片设置最大尺寸&#xff0c;降低大小后&#xff0c;再次发布&#xff0c;zip文件缩减到250M 二、如何一键获得工程里面的精灵图片信息 三、获取精灵图片信息 1、…

esp32-S3-electric-vehicle-expansion(EVE_V2)硬件分享

一. 简介 本次将给大家分享一个QSPI圆形屏幕DIY的小项目&#xff0c;这是我做的第二个版本的&#xff0c;相较于第一个版本有了比较大的改动(第一版就不放出来了&#xff0c;需要的可以私聊)&#xff0c;可以在上面实现更多的功能&#xff0c;做些更有趣的项目 &#xff0c;也…

[libc-2.31 off_by_null] N0wayBack ezheap练习

以前保留了个WP&#xff0c;但是没复现过也没法用&#xff0c;用了两个晚上慢慢理复现一下。 先看这个题 while ( 1 ){menu();__isoc99_scanf("%d", &v3);switch ( v3 ){case 1:m1add(); //带readbreak;case 2:m2free();break;case 3:m3edit(); //溢出br…

视频监控系统/安防监控/视频AI智能分析:小动物识别算法场景汇总

随着人们对生态环境的关注日益提升&#xff0c;大家对动物保护意识也逐渐增强。旭帆科技智能分析网关小动物识别算法应运而生。除了对保护动物的识别以外&#xff0c;旭帆科技AI智能分析网关还可以识别常见的老鼠等动物&#xff0c;助力明厨亮灶监管&#xff0c;保卫食品安全。…

uniapp风险等级(三级)

代码 ​ <template><view><view class"riskGrade"><label>风险等级: </label><span v-if"flag 0 || flag 1 || 2" class"item":style"[{background:flag0?color:flag1?color:flag2?color:}]"…

Redis 事务 - 监控测试

Redis 基本事务操作 Redis事务本质&#xff1a;一组命令的集合&#xff01;一个事务中的所有命令都会被序列化&#xff0c;在事务执行过程的中&#xff0c;会按照顺序执行&#xff01; Redis事务是一组Redis命令的有序集合&#xff0c;这些命令在事务中按照顺序执行&#xff0…

voliate实战:voliate可见性验证有序性非原子性验证

一、可见性验证 下面的程序验证了voliate的可见性。 public class VolatileVisibilityTest {private static volatile boolean inintFlag false;public static void main(String[] args) throws InterruptedException {new Thread(() -> {System.out.println("waiti…

期权投资的优势有哪些方面?

随着金融市场的不断演变&#xff0c;越来越多的金融衍生品出现在人们的视线中&#xff0c;特别是上证50ETF期权可以做空T0的交易模式吸引了越来越多的朋友&#xff0c;那么期权投资的优势有哪些方面&#xff1f; 期权是投资市场中一个非常重要的投资方式&#xff0c;期权投资能…

LeetCode:两数之和

题目描述&#xff1a; 这是一道用暴力解法&#xff0c;逻辑十分简单、清晰的一道题&#xff0c;直接遍历数target-num[i]就行 而官方给了第二种巧妙的解法&#xff1a;运用哈希表。此法可将时间复杂度从O&#xff08;N^2&#xff09;降到O&#xff08;1&#xff09; 其思路是…

日志技术-Logback

日志技术 将系统执行的信息&#xff0c;方便的记录到指定位置&#xff08;控制台、文件、数据库&#xff09;可以随时以开关的形式开关日志&#xff0c;无需入侵到源代码去修改 日志接口&#xff1a;设计日志框架的统一标准 注&#xff1a;有人对JCL接口不满意&#xff0c;就…

基于Questasim的SystemVerilog DPI使用流程

1. 前言 DPI是Direct Programming Interface的缩写&#xff0c;它提供了SystemVerilog与其它编程语言(特别是C语言)交互的接口。它允许编程人员轻松地从SystemVerilog调用C函数&#xff0c;且在C函数也可以调用Systemverilog的函数。 DPI极大地方便了使用现有的C代码&#xf…

深度学习-全连接神经网络-激活函数- [北邮鲁鹏]

文章目录 基础知识为什么需要非线性操作&#xff08;激活函数&#xff09;&#xff1f;激活函数 vs 数据预处理常用的激活函数Sigmoid函数 &#xff08;Logistic函数&#xff09;双曲正切函数&#xff08;Tanh函数&#xff09;线性整流函数&#xff08;ReLU函数&#xff09;Lea…

C【数组】

1.一维数组 1.1 数组的创建 1.2 数组的初始化 1.3 一维数组的使用 int main() { // char arr[] "abcdef";//[a][b][c][d][e][f][\0] // //printf("%c\n", arr[3]);//d // int i 0; // int len strlen(arr); // for(i0; i<len; i) // { // p…

机器学习笔记之最优化理论与方法(十)无约束优化问题——共轭梯度法背景介绍

机器学习笔记之最优化理论与方法——共轭梯度法背景介绍 引言背景&#xff1a;共轭梯度法线性共轭梯度法共轭方向共轭VS正交共轭方向法共轭方向法的几何解释 引言 本节将介绍共轭梯度法&#xff0c;并重点介绍共轭方向法的逻辑与几何意义。 背景&#xff1a;共轭梯度法 关于…

Ubuntu 22.04LTS + 深度学习环境安装全流程

一、 CUDA Toolkit 安装 1. 选择需要安装的版本(下载地址) 2. 选择自己的系统版本获取下载地址和安装指令 3. 运行安装指令进行安装 wget https://developer.download.nvidia.com/compute/cuda/12.2.2/local_installers/cuda_12.2.2_535.104.05_linux.run sudo sh cuda_12.2.…

Docker Swarm集群部署

Docker Swarm集群部署 任务平台 3台虚拟机&#xff0c;一台作为manager 节点&#xff0c;另两台作为work节点。 文章目录 Docker Swarm集群部署安装docker配置防火墙开放端口在 manager 节点创建 Swarm 集群创建用于swarm服务的自定义的overlay网络测试跨主机容器通信 安装do…

网上办公系统设计与实现

目录 前言 1问题定义 1.1系统名称 1.2系统背景 1.3系统目标 2 可行性分析 2.1 经济可行性 2.2 技术可行性 2.3 操作可行性 2.4 法律可行性 2.5 可行性研究结论 2.6 用户组织机构图 2.7 目标系统业务流程图 2.8 接口设计 2.8.1外部接口 2.8.2 内部接口 3 需求分…

Centos7.9 一键脚本部署 LibreNMS 网络监控系统

前言&#xff1a; LibreNMS 是个以 PHP/MySQL 为基底的自动探索网络监控系统 LibreNMS 官网 版本23.8.2-52-g7bbe0a2 - Thu Sep 14 2023 22:33:23 GMT0700数据库纲要2023_09_01_084057_application_new_defaults (259)Web 服务器nginx/1.20.1PHP8.1.23Python3.6.8DatabaseMa…