深入Android系统(三)Binder-1-导读与简介

导读(总结)

导读叕叕叕叕来了。

本来计划着8月底看完binder,而且是用一篇文章来记录完所学的知识,可是:

  • 读着读着,感觉缺少一个能串联起整个binder的主线,所以决定增加导读
  • 写着写着,突然发现一篇装不下,所以拆成了三篇

简单的开始

我们先简单了解下binder架构涉及的几个概念:

  • 客户进程:发起远程方法调用的进程
  • 服务进程:真正服务所运行的进程
  • binder驱动binder通信的核心,远程调用都是通过它来处理和传递的
  • servicemanager:一个管理binder服务的进程

一个简单版本的跨进程调用的流程:

  • 客户进程把调用信息发给驱动,等待驱动返回消息
  • 驱动处理一下调用信息,并通知服务进程
  • 服务进程接到驱动的通知,根据调用信息开始执行
  • 服务进程执行完毕,通知驱动
  • 驱动接收到服务进程的通知,对数据进行简单处理,通知客户进程读取数据
  • 客户进程接收到通知,读取驱动返回的数据

需要简单区分的两个名词:

  • 实现一个Binder服务:利用Binder来实现一个自己的IPC的服务(怎么使用Binder
  • Binder的实现:Binder的实现原理,涉及到学习binder驱动部分了

Binder的一些疑问

第一个问题,客户进程要调用服务进程的函数,怎么确认要调用哪一个服务呢?又怎么知道要调用哪个函数呢?

我们先看几个知识点:

  • Binder服务的使用上,Android 定义了一些模板类
    • 服务进程中,如果你写的服务想要支持Binder远程调用,就必须按我这样写
      • 对于C++层,要手动实现Bn...Bp...等类(不知道咋写?Android中那么多支持IPC的服务,借鉴呗。好消息是现在有个HIDL可以简化开发)
      • 对于Java层,因为AIDL的存在,会有脚本自动生成Bn...Bp...等类,只需要在回调中实现业务逻辑就可以
    • 而对于客户进程,如果想完成一个远程调用,那就在编程阶段必须
      • 对于C++层,要得到想要调用服务的Bp.....类型信息才可以,在编程阶段就要写进客户进程
      • 对于Java层,抄一份想要调用的服务进程AIDL客户进程,重新编译一下就可以获得对应服务的接口类了
    • 对于客户进程,还有一种完成远程调用的方式,就是我们使用的各种...Manager
      • c++层和AIDL一样,我们在客户进程也是通过对于...Manager类的接口来完成的调用
  • 想象一个自由的通信状态应该是:给我个binder驱动我可以忍受,干嘛要集成你的服务接口类啊,这么多条条框框的。我想调用哪家的IPC服务,直接通过驱动发给他就可以了哇。
    • too young,too naive,本人考虑并不周全,只是举例哈:
      • 如何取得要调用的服务对象?后台服务千千万,就算不取得,也得要通知这个服务吧
      • 如何传递要调用哪个方法呢?
      • 如何获得远程服务有多少方法呢?
      • 服务进程在编写时除了完成业务逻辑还需要单独和驱动交流工作。
    • 这些问题,Android通过自己设计出来的的Binder相关的模板类都解决了
      • 不知道服务对象在哪里?
        • 那就在客户进程加上我设计好的Bp...类,里面有提前定义好的接口asInterface,直接转换就好了
      • 不知道有哪些可以远程调用的方法?
        • 都给你Bp...类了,还要啥自行车
      • 用和binder驱动通信么?
        • 通啥信啊,封装好了,直接调用方法就行了
  • 在这里回答第一个问题
    • 在编程时,我们根据需求,把需要调用的IPC服务的Bp...类方到我们的客户进程
      • Bp...类有个asInterface方法,可以通过这个方法转化为我们需要的代理对象
    • 而关于调用方法的指定,binder 会在Bp...Bn...模板类中定义出调用的函数号来
      • 客户进程有了Bp...类,也就有了调用的函数号

第二个问题,我们自己写的服务什么时候启动的呢?

从常理来讲,我们的服务要在进程启动的时候就要开始监听外界的消息了,不然万一错过消息怎么办。

Binder也是这样做的:

  • 对于服务进程来说
    • 会在进程启动时执行joinThreadPool
      • 这个函数会进入while循环读取binder驱动的数据
  • 对于客户进程来说
    • 在发送远程调用时,会执行transact函数时
      • 这个函数在发送完调用数据给binder驱动后,会继续读取,查看返回数据

servicemanager 怎么没占多少篇幅呢?

首先这不是一个问题,哈哈哈哈!

  • servicemanagerAndroid用来管理注册的Binder服务的,我们的getService大概率是找的它。(还没没看源码,不要轻信)
  • servicemanager并没有按照Android定义的模板来,自己实现了一个简易版本的
  • servicemanager在Android系统启动时就启动了,这部分在init.rc中配置
  • 没有servicemanager不影响我们认识学习binder整体流程和结构
  • 有了servicemanager,可以加深我们对binder的理解

导读结束

binder导读部分对一些知识点的描述比较简单。细节还是看正文

  • 书本中源码是Android 5.0,但是binder这部分从9.0上看也没有太大变化,只是增加了一些log打印
  • 这代码强悍的生命力不正是我们追求的么,哈哈哈
  • 本来还想在导读中说一下一次拷贝的事情,算了,留在正文吧

binder的学习比时间晚了两周。。。。。

好在真滴可以学到不少东西,下面是正文,谨慎阅读


Binder 简介

Binder是Android特有的一种进程间通信方式。Android Binder的前身是 OpenBinder,最早由 Dianne Hackborn 开发并用于PalmOS上,后来Dianne Hackborn加入了Google,在OpenBinder的基础上开发了Android Binder

Binder和传统的IPC机制相比,融合了远程过程调用(RPC)的概念,而且这种远程调用不是传统的面向过程的远程调用,而是一种面向对象的远程调用。

从Unix发展而来的IPC机制,只能提供比较原始的进程间通信手段,通信的双方必须处理线程同步、内存管理等复杂问题,不但工作量大,而且容易出错。

除了Socket、匿名管道以外,传统的IPC,例如命名管道、信号量、消息队列等已经从Android中去掉了。

和其他IPC相比,Socket是一种比较成熟的通信手段,同步控制也很容易实现。Socket用于网络通信非常合适,但是用于进程间通信,效率就不太高了。

Android在架构上一直希望模糊进程的概念,取而代之以组件的概念。应用不需要关系组件的存放位置、运行在哪个进程、生命周期等问题。随时随地,只要拥有Binder对象,就能使用组件功能。Binder就像一张网,将整个系统的组件,跨越进程和线程,组织在了一起。

Binder很强大也很复杂。但我们在使用时却是一件很轻松的事情,不需要考虑线程同步、内存分配等问题。这些复杂的事情都在Binder框架中完成了。

正因为如此,Android系统的服务都是利用Binder构建的。Android系统服务有几十种之多,这是任何一个其它嵌入式平台所不具备的。

Android在进程间传递数据使用的是共享内存的方式,这样数据只需要复制一次就能从一个进程到达另一个进程(一般的IPC都需要两步,从用户进程复制到内核,再从内核复制到服务进程),大大提高了数据传输效率。

在安全性方面Android也做了考虑,Binder调用时会传递进程的euid到服务端,因此,服务端可以通过检查调用进程的权限来决定是否允许其使用所调用的服务。

大家可以阅读下知乎大神的 为什么 Android 要采用 Binder 作为 IPC 机制? 来加深下了解,文章有惊喜

Binder对象定义

为了理解方便,把Binder相关的对象进行如下定义:

  • Binder实体对象Binder实体对象就是Binder服务的提供者。一个提供Binder服务的类必须继承BBinder类。因此,有时为了强调对象的类型,也用BBinder对象来代替Binder 实体对象
  • Binder引用对象Binder引用对象Binder实体对象在客户进程的代表,每个引用对象的类型都是BpBinder类。同样可以用名称BpBinder对象来代替Binder引用对象
  • Binder代理对象Binder代理对象也称为Binder接口对象,它主要为客户端的上层应用提供接口服务,从IInterface类派生,它实现了Binder服务的函数接口,当然只是一个转调的空壳。通过Binder代理对象应用能像使用本地对象一样使用远端的Binder实体对象提供的服务。
  • IBinder对象BBinderBpBinder类都是从IBinder对象继承而来的。在很多场合,不需要刻意地去区分Binder实体对象Binder引用对象,这是刻意使用IBinder对象来统一称呼它们。

Binder接口对象主要和应用程序打交道,将Binder接口对象Binder引用对象分开的好处就是Binder接口对象可以有很多实例,但是它们包含的是同一个Binder引用对象这样方便了用户层的使用

关系图如下:
image

应用完全可以抛开Binder接口对象直接使用Binder引用对象,但是这样开发的程序兼容性不好。也正是因为在客户端将Binder引用对象Binder接口对象分离,Android才能用一套架构来同时为Java层Native层提供Binder服务。隔离后Binder底层不需要关心上层的实现细节,只需要和Binder实体对象Binder引用对象进行交互。

Binder的架构

Binder通信的参与者由4部分组成:

  • Binder驱动Binder的核心,实现各种Binder的底层操作。
  • ServiceManager:提供Binder 实体对象的名称到Binder 引用对象的转换服务
  • 服务端Binder服务的提供者
  • 客户端Binder服务的使用者

关系图如下:
image

Binder 驱动的简单解释:

  • Binder 驱动属于Binder架构的核心,通过文件系统内的标准接口,如openioctlmmap等,向用户层提供服务。
  • 应用层和Binder 驱动之间的数据通过ioctl接口来完成,并没有使用readwrite系统调用。使用ioctl的好处就是一次系统调用就可以完成用户系统和驱动之间的双向数据交换,不但简化了控制逻辑,也提高了传输效率。
  • Binder 驱动的主要功能就是提供Binder通信的通道,维护Binder对象的引用计数,转换传输中的Binder实体对象Binder引用对象以及管理数据缓存区。

ServiceManager的简单解释:

  • ServiceManager是一个守护进程,它的作用是提供Binder服务的查询功能,返回被查询服务的引用。
  • 对于一个Binder服务,通常会有一个唯一的字符串标识,只要它向ServiceManager注册了这个标识,应用就可以通过标识来获得Binder服务的引用对象。
    • ServiceManager是一个独立的进程,对于其他想要获得Binder服务的进程来说,首要任务是获得ServiceManager
    • ServiceManager也是通过Binder框架来提供服务的,感觉遇到了鸡生蛋蛋生鸡的情况
    • 为了保证其他进程能获得ServiceManager,Android默认注册绑定了ServiceManagerBinder引用对象的核心数据是一个Binder实体对象的引用号,它是在驱动内部分配的一个值。Binder框架硬性规定了0代表ServiceManager。这样用户进程使用参数0可以直接构造出ServiceManagerBinder引用对象
  • 为什么查找Binder服务功能不直接放到Binder驱动中完成呢?
    • 是Android的安全管理要求,不容许任意进程都能注册Binder服务,虽然任意进程都能创建Binder服务,但只有root进程system进程才可以不受限制地向ServiceManager注册服务。
    • ServiceManager中有一张表,里面规定了能够注册服务的进程的用户ID,以及能够注册的服务名称。

Binder服务可以分成两种:实名服务匿名服务。除实名服务能够通过ServiceManager查询到外,它们从开发到使用没有任何其他区别。

对于普通应用开发的Binder服务,都是匿名服务。对于匿名服务无法通过ServiceManager获取,那么客户端通过什么方式才能获得Binder引用对象呢?

  • 首先,匿名Binder服务的传输必须基于已经建立好的Binder通信连接,也就是基于实名注册的Binder
  • Server匿名Binder对象写入Parcel,接着通过ioctlbinder驱动通信,bidner驱动会对传入的对象进行检查。
  • 如果发现传入的typebinder,则会在当前进程(Server对应的proc)查找是否有这个节点
    • 如果没有,就用传入的匿名Binder对象在用户空间的指针cookie(通常是BBinder本身)构造一个新的节点,接着得到匿名Binder对象的引用,存放在transaction_data中等待Client去获取。
  • Client得到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。

匿名Binder这部分暂时收集到的资料就是这些,等学习完后面的再来补充吧

组件Service和匿名Binder服务

Android组件Service包含了一种启动Java匿名Binder服务的方法,我们来简单看下

  • 首先,需要应用调用bindService方法发出一个Intent
  • Framework根据Intent找到对应的组件Service并启动它,此时组件Service匿名Binder服务也将同时创建出来。
  • 随后,Framework会把服务的IBinder对象通过ConnectivityManager的回调方法onServiceConnected()传回到应用
  • 这样应用就得到了匿名Binder服务的引用对象

在这里Android的FrameworkIntent代替了Binder服务的名称来查找对应的服务,同时也承担了ServiceManager的工作,解析Intent并传回服务的引用对象。如图:
image

Android的Framework并没有使用新的技术来传递Binder对象,因为Framework中担任这个角色的ActivityManagerService本身就是一个Binder服务,最终还是通过Binder框架在服务端和客户端之间传递了IBinder对象

Binder的层次

从代码实现上划分,Binde设计的类可以分为4个层次,如图:
image

  • 最上层是位于Framework中的各种Binder服务类和他们的接口类。这一层的类非常多,例如:ActivityManagerServiceWindowManagerServicePackageManagerService等,它们为应用程序提供了各种各样的服务。
  • 第二层是用于给最上层的服务类和接口类的开发提供基础,如IBinder、BBinder、BpBinder等。
  • 第三层是和Binder驱动交互的IPCThreadStateProcessState
  • 最底层是Binder驱动

从图上看,libbinder被拆分成了两个层次,主要原因是:

  • 第一层和第二层联系很紧密,第二层中的各种Binder类用来支撑服务类和代理类的开发
  • 但是第三层的IPCThread和第四层的驱动耦合的很厉害

这些正是Binder架构被人诟病的地方,驱动和应用层之间过于耦合了,违反了Linux驱动设计的原则,因此,主流的Linux并不愿意接纳Binder

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值