深入 Android 系统 Binder-1- 导读与简介 (1)
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 怎么没占多少篇幅呢?
首先这不是一个问题,哈哈哈哈!
servicemanager
是Android
用来管理注册的Binder服务
的,我们的getService
大概率是找的它。(还没没看源码,不要轻信)servicemanager
并没有按照 Android 定义的模板来,自己实现了一个简易版本的servicemanager
在 Android 系统启动时就启动了,这部分在init.rc
中配置没有
servicemanager
不影响我们认识学习binder
整体流程和结构有了
servicemanager
,可以加深我们对binder
的理解
binder导读
部分对一些知识点的描述比较简单。细节还是看正文
书本中源码是
Android 5.0
,但是binder
这部分从9.0
上看也没有太大变化,只是增加了一些log
打印这代码强悍的生命力不正是我们追求的么,哈哈哈
本来还想在导读中说一下
一次拷贝
的事情,算了,留在正文吧
binder
的学习比时间晚了两周。。。。。
好在真滴可以学到不少东西,下面是正文,谨慎阅读
=======================================================================
Binder
是 Android 特有的一种进程间通信方式。Android Binder
的前身是OpenBinder
,最早由Dianne Hackborn
开发并用于PalmOS
上,后来Dianne Hackborn
加入了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
服务的类必须继承BBinder
类。因此,有时为了强调对象的类型,也用BBinder对象
来代替Binder 实体对象
。Binder引用对象
:Binder引用对象
是Binder实体对象
在客户进程的代表,每个引用对象的类型都是BpBinder
类。同样可以用名称BpBinder对象
来代替Binder引用对象
。Binder代理对象
:Binder代理对象
也称为Binder接口对象
,它主要为客户端的上层应用提供接口服务,从IInterface
类派生,它实现了Binder
服务的函数接口,当然只是一个转调的空壳。通过Binder代理对象
应用能像使用本地对象一样使用远端的Binder实体对象
提供的服务。IBinder对象
:BBinder
和BpBinder
类都是从IBinder
对象继承而来的。在很多场合,不需要刻意地去区分Binder实体对象
和Binder引用对象
,这是刻意使用IBinder对象
来统一称呼它们。
Binder接口对象
主要和应用程序打交道,将Binder接口对象
和Binder引用对象
分开的好处就是Binder接口对象
可以有很多实例,但是它们包含的是同一个Binder引用对象
这样方便了用户层的使用。
关系图如下:
应用完全可以抛开Binder接口对象
直接使用Binder引用对象
,但是这样开发的程序兼容性不好。也正是因为在客户端将Binder引用对象
和Binder接口对象
分离,Android 才能用一套架构来同时为Java层
和Native层
提供Binder
服务。隔离后Binder
底层不需要关心上层的实现细节,只需要和Binder实体对象
和Binder引用对象
进行交互。
Binder
通信的参与者由 4 部分组成:
Binder驱动
:Binder
的核心,实现各种Binder
的底层操作。ServiceManager
:提供Binder 实体对象
的名称到Binder 引用对象
的转换服务服务端
:Binder
服务的提供者客户端
:Binder
服务的使用者
关系图如下:
Binder 驱动
的简单解释:
Binder 驱动
属于Binder
架构的核心,通过文件系统内的标准接口,如open
、ioctl
、mmap
等,向用户层提供服务。应
用层和Binder 驱动
之间的数据通过ioctl
接口来完成,并没有使用read
和write
系统调用。使用ioctl
的好处就是一次系统调用就可以完成用户系统和驱动之间的双向数据交换,不但简化了控制逻辑,也提高了传输效率。
Binder 驱动
的主要功能就是提供Binder
通信的通道,维护Binder
对象的引用计数,转换传输中的Binder实体对象
和Binder引用对象
以及管理数据缓存区。
ServiceManager
的简单解释:
ServiceManager
是一个守护进程,它的作用是提供Binder
服务的查询功能,返回被查询服务的引用。对于一个
Binder
服务,通常会有一个唯一的字符串标识,只要它向ServiceManager
注册了这个标识,应用就可以通过标识来获得Binder
服务的引用对象。ServiceManager
是一个独立的进程,对于其他想要获得Binder
服务的进程来说,首要任务是获得ServiceManager
。ServiceManager
也是通过 Binder 框架来提供服务的,感觉遇到了鸡生蛋蛋生鸡的情况为了保证其他进程能获得
ServiceManager
,Android 默认注册绑定了ServiceManager
:Binder引用对象
的核心数据是一个Binder实体对象
的引用号,它是在驱动内部分配的一个值。Binder框架
硬性规定了0
代表ServiceManager
。这样用户进程使用参数0
可以直接构造出ServiceManager
的Binder引用对象
。为什么查找
Binder
服务功能不直接放到Binder驱动
中完成呢?是 Android 的安全管理要求,不容许任意进程都能注册
Binder
服务,虽然任意进程都能创建Binder
服务,但只有root进程
或system进程
才可以不受限制地向ServiceManager
注册服务。ServiceManager
中有一张表,里面规定了能够注册服务的进程的用户 ID,以及能够注册的服务名称。
Binder
服务可以分成两种:实名服务
和匿名服务
。除实名服务
能够通过ServiceManager
查询到外,它们从开发到使用没有任何其他区别。
对于普通应用开发的Binder
服务,都是匿名服务
。对于匿名服务
无法通过ServiceManager
获取,那么客户端通过什么方式才能获得Binder引用对象
呢?
首先,
匿名Binder服务
的传输必须基于已经建立好的 Binder 通信连接,也就是基于实名注册的Binder
Server
将匿名Binder对象
写入Parcel
,接着通过ioctl
和binder驱动
通信,bidner驱动
会对传入的对象进行检查。如果发现传入的
type
是binder
,则会在当前进程(Server
对应的proc
)查找是否有这个节点如果没有,就用传入的
匿名Binder对象
在用户空间的指针
和cookie
(通常是BBinder
本身)构造一个新的节点,接着得到匿名Binder对象
的引用,存放在transaction_data
中等待Client
去获取。Client
得到这个匿名Binder
的引用,通过这个引用向位于Server
中的实体发送请求。
匿名 Binder 这部分暂时收集到的资料就是这些,等学习完后面的再来补充吧
Android 组件
Service
包含了一种启动 Java 匿名 Binder 服务的方法,我们来简单看下
首先,需要应用调用
bindService
方法发出一个Intent
Framework
根据Intent
找到对应的组件Service
并启动它,此时组件Service
中匿名Binder服务
也将同时创建出来。随后,
Framework
会把服务的IBinder
对象通过ConnectivityManager
的回调方法onServiceConnected()
传回到应用这样应用就得到了
匿名Binder服务
的引用对象
评论