原文地址https://www.nds.rub.de/media/attachments/files/2011/10/main.pdf
本章介绍什么是Binder以及Binder的能力。本章是从抽象的角度介绍Binder,并没有涉及到实现细节,在下一章我们会讲解实现细节。
Binder最初是Be Inc公司开发的OpenBinder,后来Palm Inc收购了Be Inc,这个项目随之转到了Pam公司下。主开发人员是Dianne Hackborn。在OpenBinder的文档中声称:Openbinder是一个系统级的架构,为传统的现代操作系统服务提供一个丰富的高级抽象。
更准确的说,Binder提供了对不同执行环境的粘合能力。OpenBInder实现运行在LInux之上,扩展了现在的IPC机制。OpenBinder文档中还声称:OpenBinder已经能移植到多种平台,包括BeOS, Windows,已经PalmOS Cobalt
Android上的Binder是OpenBinder的定制。在Android's M3 release 原生的kernel包含了OpenBinder的驱动部分,但是Binder的用户空间部分和OpenBinder相比被重写了,以避免OpenBinder的License和Android's的License不相容的情况。在Android's M5 release中,驱动部分也被重写了。尽管核心概念没有改变,但是很多实现细节都改变了。
原始的OpenBinder代码不再开发了。但是从某种程度上说,Android binder可以看作是OpenBinder的一个分支,被Android开发者维护着。
Binder framework使用它自己的数目来命名它的功能和组件。这一节介绍几个最重要的属于,细节部分会在后面讨论。
Binder 这个术语的使用一直比较模糊,有时指整个Binder框架,有时指一个Binder接口的特定实现。
Binder Object 是一个实现Binder接口的类实例。一个Binder对象可以实现多个Binders
Binder Protocol Binder中间件使用了一个非常低级的协议,用来和Binder kernel driver通信。
IBinder interface 一个Binder interface是Binder可以实现的一组定义良好的方法,属性和事件。通常使用AIDL语言描述。
Binder Token 可以用来惟一标始一个Binder的数字。
Binder framework 提供了一个简单的进程间通信系统,
从Android应用开发者的角度,最重要的改变就是可以想调用本地对象方法一样,调用远程对象方法。这是一个同步方法调用。相应的,client进程在服务器给出应答之前保持block状态。这样做的好处是避免异步调用导致client要提供一个线程方法。
AIDL的一个特色就是,在高的的层次,应用不需要知道服务试运行在一个server进程还是就在进程内部。Android's的应用概念使得在进程内还是Activity进程运行服务称为可能,这使得应用开发者可以很容易的把自己的服务export给其他的Android应用。
Android系统服务使用Binder的一个特定的通知功能,称为link to death机制。这个机制允许一个进程 获得给定进程的binder结束通知。特别是这个方法允许Android window manager建立一个link to death关联到每一个window的Binder接口,使得window被关闭时通知windwo manager。
每一个Binder是唯一标始的,这意味着它可以用做共享令牌。我们假定Binder没有通过Service Manager发布,BInder标始仅仅被通讯参与者知悉,包括远程进程和本地进程。相应的一个Binder可以被看作一个安全访问令牌。令牌可以被多个进程共享。
另外一个安全特色是:被调用进程可以通过UID和PID来标始调用进程。结合Andoird的安全模型,一个进程可以被标始。另外一个特色,是共享内存机制,通过Binder framework可以共享heap
总的来说,Binder框架的特色使得它良好的支持面向对象的进程间通信。
本节描述这些设施后面的概念,由于Binder缺乏公开的文档,所以本节大部分都是基于阅读代码后的理解。
Binder framework通信是client/server模型。一个client发起通信并等待Server的响应。Client端使用一个client-side proxy来进行通信。在服务器端,一个线程池来满足client发送来的请求。在图4.2中,client端进程A包括一个proxy对象,实现和Binder kernel driver的通信。Process B是一个服务器端进程,包含多个Binder线程。Binder framework创建新的线程处理所有进入的请求,直到最大允许线程数。代理对象和Binder driver通信,Bind driver负责把消息转发给目标对象。
如果一个进程发送数据给其他的进程,那么称为transaction。伴随着每一次传输payload数据被提交。这些数据称为transaction data。
数据结构如图4.3。它包含一个target成员,也就是目标binder node;cookie成员用来内部信息;sender ID成员包含安全相关的信息;data成员包含一组数据,每个数组项包含一个命令和命令参数,Binber会解析这个数组。在Binder framework之上的application可以用这些参数定义它们自己的命令。在Android源代码中我们可以看到,开发者使用target command成员作为远程服务的函数指针,并且把函数的参数序列化到argument成员中。就是实现了一个用户自定义IPC协议。Binder通信依赖于request和reply机制。这就意味着每一个请求比去于一个应答,而请求进程则必须要等待应答,在等待的过程中,请求进程处于阻塞状态。如果开发者想要实现异步的IPC协议,当然是可行的,但是需要开发者注意Binder本身并不支持异步IPC,开发者必须自己实现一个机制来管理请求应答。
因此一个transaction意味着需要两条message,transaction请求和transaction reply。特例是one way通信,一路通信仅限于death notification这种功能。
从对象的观点来说,transaction data被称为parcel。换句话说,传输的是数据结构。任何能够传输到远端的对象,都要必须实现parcelable接口。一个实现parcelable接口的对象必须提供序如下方法,在sender端能够序列化对象数据结构,并且在接收端恢复为对象数据结构。数据结构的所有成员都被简化为简单的数据类型如Int, Float,Boolean和String,然后被序列化到一个parcel中。
创建parcel的过程称为marshaling或者对象平面化,相反,从parcel重建object的过程称为unmarshaling或者去平面化
Binder的内存共享基础设施不能被JAVA 应用使用,仅仅C++库可以访问内存中的共享对象表示。
Binder framework支持Binder object的死亡通知,这是一种观察者模式。一个本地BInder 对象如果想知道一个远程Binder对象的终结,那么就把它加到观察着列表中。如果一个发生的事件是远程Binder对象销毁,那么本地对象就会得到通知执行相应的动作。图4.4演示了这一过程
Context manager是Binder framework中的一个特别Binder node。他对应的Binder号是0。它是一个name system,这样就可以使用一个名称来表示一个Binder接口。这非常重要,因为Clients一般不会预先知道远程Binder的地址。如果一个client预先知道了remote binder的地址,那么Binder的安全令牌机制就不能工作了。但是如果不知道远程通信伙伴的地址,那么就无法发起通信。而context manager解决了这个问题,接口地址固定并且地址是知名的。Context manager实现是Binder framework的一部分。在Android中,context manager被称为Service manager。每一个需要提供服务的Binder都要提交他的名字和Binder令牌给Server manager。而对于client端来说,仅仅需要知道服务的名字,通过名称从service manager查询所要使用服务的Binder address。
intent也就是message,开发者在Java API这一层使用它,并通过IPC发送。它是一个要执行动作的抽象表示。抽象意味着动作的执行者并没有定义在intent中。Intent仅仅包含action和data。如4.5给出来一个intent的例子。这个intent由Binder的intent reference monitor分发。service将负责执行拨号动作,所拨拨号码就是保存在Data中
有两种形式的intents,显示的intent是指一个特定的组件。另一方面,一个隐式的intent把决定权交给Android系统。如果系统有多个部件可以处理它,那么Android 系统会选择最合适的组件运行这个intent
Binder在Android平台上使用的相当多,无论什么时候两个进程需要通信, Binder都会介入。比如window manager通过Binder framework和它的clients交换数据。同时使用Binder的death notification功能来获得client应用的结束信息。
s所以Binder连接Android系统的各个框架,因此是非常重要的一个模块。
Binder安全模型非常简单但是很有效。它确保进程间通信通道的安全,保证通信方的身份识别。
伴随Intent的另外一个功能是intent filter。Service和app可以声明,告诉系统哪些intents可以转发给他们执行。
本章会大概分析Binder framework的实现。对于每一层,源代码文件在[10] [9]中列出来,我们讨论他们的目的。此外还有AIDL,因为它被用来生成Java code,所以也可以看作是Binder framework的一部分。
图5.1演示了Binder framework的不同层次。它有三层组成。第一层也就是最高一层是Android应用API。第二层Binder framework中间件层;第三层也就是最底层则是kernel driver
Android interface definition language AIDL是Eclipse SDK的一部分,由Google提供。它的主要目的是简化Android 远程服务的实现。AIDL使用类Java的语法。
在AIDL文件中,开发者用远程服务的方法签名定义接口。AIDL 解析器可以从接口生成Java class,首先为client 生成访问service的代理类,其次为service实现生成一个stub类,service可以扩展这个类做为远程方法的实现。
AIDL语言支持基本数据类型。它生成的代码可以实现:写值到parcels,通过Binder IPC发送 parcels,接收parcel,读取这些值,调用服务的方法,并且发送执行结果。
AIDL文件必须由远程service/app开发者和client app开发者共享。因为AIDL生成器为client和service生成的代码保存在一个文件中,每一个应用使用和实例化的仅仅是这些生成类的一个子集。
本节讨论Java framework,这个框架位于middleware和kernel driver之上。这些source classes和interfaces属于Java API层:
Interfaces:
• android.app.IActivityManager
• android.os.Parcable
• andorid.os.IBinder
• android.content.ServiceConnection
Classes:
• android.app.ActivityManagerNative
• android.app.ContextImpl
• android.content.Intent
• android.content.ComponentName
• android.os.Parcel
• android.os.Bundle
• android.os.Binder
• android.os.BinderProxy
• com.android.internal.os.BinderInternal
Binder framework的Java layer有两个功能。一个功能是对下层中间件的封装,使得Android应用可以参与到Binder通信当中。第二个功能,它在Binder framework中引入了intents功能。
Java API Layer依赖于Binder中间件。为了从Java使用C++编写的中间件,必须使用JNI。在源代码文件frameworks/base/core/jni/android_util_Binder.cpp中,实现了Java和C++函数之间的映射。
Binder framework在用户空间的中间件实现是C++编写的。framework提供了进程和线程控制方法以及 孵化和管理新线程所需要的数据结构。Marshalling和Unmarshalling功能也是在这里实现的,所以对象信息可以在这里转换为可提交的parcel数据。中间件提供了和Binder kernel驱动的交互以及共享内存的实现。
使用C++的Services或者apps可以直接使用Binder framework,但是必须放弃Java API提供的那些功能。
源代码保存在这些文件中:
• frameworks/base/include/utils/IInterface.h
• frameworks/base/include/utils/Binder.h
• frameworks/base/include/utils/BpBinder.h
• frameworks/base/include/utils/IBinder.h
• frameworks/base/include/utils/Parcel.h
• frameworks/base/include/utils/IPCThreadState.h
• frameworks/base/include/utils/ProcessState.h
• frameworks/base/libs/utils/Binder.cpp
• frameworks/base/libs/utils/BpBinder.cpp
• frameworks/base/libs/utils/IInterface.cpp
• frameworks/base/libs/utils/ProcessSTate.cpp
• frameworks/base/libs/utils/IPCThreadState.cpp
Binder kernel driver是Binder framework的核心。在这个点上,信息的可靠性和安全交付必须得到满足。kernel driver是一个使用C语言实现的内核模块。驱动代码包含下面的源文件
• /drivers/staging/android/binder.c
• /drivers/staging/android/binder.h
Binder kernel驱动支持下面的文件操作open, mmap, release, poll和系统调用ioctl。这个操作代表着上层访问Binder驱动的接口。Binder操作open建立到Binder的连接并且把这个连接赋给file pointer,而release操作则关闭连接。mmap操作是用来映射Binder内存的。ioctl是最主要的操作,上层通过这个接口提交和接收信息。ioctl操作使用Binder driver命令码和data buffer作为参数,这些命令包括:
BINDER _WRITE_READ 是最重要的命令,它提交一串transmission data。这个数据传报含多个数据,如图4.3描述的。
BINDER_SET_MAX_THREADS 设置每个进程服务请求的最大线程数。
BINDER_SET_CONTEXT_MGR 设置context manager。context manager仅存在一个,所以这个调用只能成功一次
BINDER_THREAD_EXIT 这个命令被中间件发送,如果一个binder线程退出。
BINDER_VERSION 返回Binder的版本号
Binder驱动实现的功能将在下一节讨论。这些节使用的命令是target commands。甚至如果名字是target command,这些命令可能仅仅应用到本地binder。在OpenBinder文档中他们被称为Binder driver protocol。下面要讨论的代码使用BC_前缀表示Binder command,使用BR前缀表示Binder return
因为kernel driver没有实现thread启动机制,它必须知道多少threads启动了。下面这些命令必须发送给Binder driver,以便Binder driver能够精确的知道looping threads数目。这些Target commands是BC_REGISTER_LOOPER, BC_ENTER_LOOPER和BC_EXIT_LOOPER。这些命令是用来进行记账的。
命令BC_TRANSACTION和BC_REPLY命令传输数据到其他的Binder接口。BC_REPLY命令被中间件用来回答一个接收到的RC_TRANSACTION。图5.3演示了当一个Binder发送一个 transaction然后等待接收这个transaction reply过程中,和Binder driver的交互过程。
Binder驱动负责把reply提交给等待线程,可以把这个reply看作直接响应。
Binder驱动从发送进程的用户空间拷贝transmission数据到kernel空间,Binder再把数据从内核拷贝到目标进程,这是通过copy_from_user和copy_to_user来实现的。data transaction过程如图5.4
Binder kernel命令BC_INCREFS,BC_RELEASE和BC_DECREFS实现Binder framework的引用计数功能。
link to death和death notification 功能也在kernel driver中实现了,kernel driver维护和管理 识别和分发Binder node结束的必要信息。命令BC_REQUEST_DEATH_NOTIFICIATION, BC_CLEAR_DEATH_NOTIFICATION, BC_DEAD_BINDER_NODE以及他们的请求码。