【Android Framework系列】第2章 Binder机制大全
Binder是Android中主要的跨进程通信方式,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案。只需要拷贝一次,基于C/S架构,易用性高,系统为每个APP分配UID同时支持实名和匿名更安全
1 Binder简介
1.1 什么是Binder
Binder是Android中主要的跨进程通信方式
。Android系统中,每个应用程序是由Android的Activity,Service,BroadCast,ContentProvider这四剑客中一个或多个组合而成,这四剑客所涉及的多进程间的通信底层都是依赖于BinderIPC机制。例如当进程A中的Activity要向进程B中的Service通信,这便需要依赖于BinderIPC。不仅于此,整个Android系统架构中,大量采用了Binder机制作为IPC(进程间通信)方案,当然也存在部分其他的IPC方式,比如Zygote通信便是采用socket。
Binder驱动和ServiceManager分别相当于网络协议中的路由器和DNS,并基于mmap
实现了IPC传输数据时只需一次拷贝。Binder包括BinderProxy
、BpBinder
、BBinder
等各种Binder实体,以及对Binder驱动操作的ProcessState
、IPCThreadState
封装,再加上Binder驱动内部的结构体、命令处理,整体贯穿Java、Native层,涉及用户态、内核态,往上可以说到Service、AIDL等,往下可以说到mmap、Binder驱动设备,是相当庞大、繁琐的一个机制。
1.2 为什么要使用Binder
为什么要用Binder???这个问题问得好。我们知道Binder是一种Android独有的跨进程通讯方式,而Android系统底层是Linux,所以Linux的跨进程通讯方式在Android里也是可以使用的。那还有其他方式可以实现Linux的跨进程通讯吗?
传统Linux进程间通信方式:管道、信号量、socket、共享内存,而Binder是android系统独有的通讯方式
。
- Binder:只需要拷贝一次,基于C/S架构,易用性高,系统为每个APP分配UID同时支持实名和匿名更安全
- 共享内存:无需拷贝,控制复杂,易用性差,依赖上层协议,访问接入点是开放的不安全
- Socket:需要拷贝两次,基于C/S架构,作为一款通用接口,其传输效率低,开销大,以来上层协议,访问接入点是开放的,不安全
- 管道:需要拷贝两次,非C/S架构,是一对一的通讯模型,把一个程序的输出直接链接另一个程序的输入。Linux下的管道主要分两种:无名管道(pipe)和有名管道(fifo)。无名管道只能用于具有亲缘关系的进程之间的通信(也就是父子进程或兄弟进程之间),是以半双工的一个通信方式,速度较慢,容量有限;有名管道是对无名管道的一种改进,可以让两个互补相干的进程之间进行通信。并且该管道在文件系统中可见,不过大小一直为0。
- 信号量:与其他的进程间通信方式不太相同,它主要提供对进程间共享资源的访问机制,进程会根据它判定是否能够访问某些共享资源,同时进程也可以修改该标志。除了用于访问控制外,还可以用于进程同步,主要是用来解决进程线程之间同步与互斥问题的一种通信机制。
Android系统需要一种高效率、安全性高的方式,因此Binder最合适不过。Binder只需要拷贝一次,效率仅次于共享内存,而且采用传统的C/S结构
2 Binder 实现机制
2.1 内存概念
首先需要明确Linux系统关于内存的几个概念:虚拟内存
,用户空间
,内核空间
、MMap
2.1.1 虚拟内存
虚拟内存简单来说,是一种内存管理技术,是虚拟的、逻辑上存在的存储空间;将物理上(不连续的物理)的内存(碎片)形成一个逻辑上连续完整的地址空间。虚拟内存初始化的过程,就叫做内存映射。我们的用户程序操作的内存实际上都是通过虚拟内存操作真正的物理内存。
简单理解:虚拟地址指向虚拟内存,虚拟内存指向对应的物理内存,物理内存会根据程序局部性原则,加载磁盘中的活跃程序到物理内存中。MMU将物理内存的地址转换为虚拟地址提供给外部(CPU)使用。虚拟内存与物理内存的映射以页为单位,常见的页大小为4KB
虚拟内存中有效位和磁盘地址可组成三种状态:
- 未分配(有效位0,磁盘地址null),磁盘上还不存在
- 未缓存(有效位0,磁盘地址有值),磁盘上存在,但内存中不存在
- 已缓存(有效位1,磁盘地址有值),磁盘、内存都存在
2.1.1.1 MMU
MMU是Memory Management Unit
的缩写,中文名是内存管理单元,有时称作分页内存管理单元(英语:paged memory management unit,缩写为PMMU)。它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制,在较为简单的计算机体系结构中,负责总线的仲裁以及存储体切换(bank switching,尤其是在8位的系统上)。
如上图所示,MMU实际上是CPU和物理内存/磁盘之间的桥梁,主要职责是将物理地址转换为虚拟地址提供给CPU使用。物理内存和磁盘之间存在频繁的读写操作,因为根据程序局部性原则
2.1.2 用户空间、内核空间
虚拟内存被操作系统划分成两块:用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。为了安全,他们是隔离的,即使用户的程序崩溃了,内核也不受影响。
32位系统,即2^32,即总共可以访问地址为4G,内核空间为1G,用户空间为3G;
64位操作系统,低位2-47位是有效的可变地址,高位48-63位全补0对应用户空间,全补1是内核空间
2.1.3 MMap(Memory Mapping)内存映射
Linux通过将虚拟内存区域与磁盘上的空间关联起来,以初始化这个虚拟内存区域的内容,这个过程成为内存映射。具体一点来说,把一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间的映射关系。实现这种映射关系之后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写到对应的磁盘文件。
用户空间(虚拟内存)和磁盘文件(物理内存)存在映射关系,这样在虚拟内存中操作文件,就会自动回写到磁盘文件中。
2.2 Binder原理
Binder通信采用C/S架构
,从组件视角来说,包含Client
、Server
、ServiceManager
以及Binder驱动
,其中ServiceManager用于管理系统中的各种服务。架构图如下所示:
可以看出无论是注册服务和获取服务的过程都需要ServiceManager
,需要注意的是此处的ServiceManager是指Native层的ServiceManager(C++)
,并非指framework层的ServiceManager(Java)
。ServiceManager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程,要掌握Binder机制,首先需要了解系统是如何首次启动ServiceManager。当ServiceManager启动之后,Client端
和Server端
通信时都需要先获取ServiceManager接口
,才能开始通信服务。
图中Client/Server/ServiceManage
之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。
注册服务(addService)
:Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。获取服务(getService)
:Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。使用服务
:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。
图中的Client,Server,ServiceManager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,ServiceManager位于用户空间。Binder驱动和ServiceManager可以看做是Android平台的基础架构
,而Client和Server是Android的应用层
,开发人员只需自定义实现Client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。
3 Binder整体架构
Client
:调用端(客户端)进程Server
:被调用端(服务端)进程BpBinder和BinderProxy
:远程Binder实体,只不过一个Native层、一个Java层,BpBinder内部持有了一个Binder句柄值handle。BBinder
:Server端接收来自Client端通过IPCThreadState传递过来的信息,然后回调onTransact实现方法。ProcessState
:进程单例,负责打开Binder驱动设备及mmap;IPCThreadState
:线程单例,负责与Binder驱动进行具体的命令通信。
通讯流程:
- 由
Proxy
发起transact()
调用,会将数据打包到Parcel中,层层向下调用到BpBinder
,在BpBinder
中调用IPCThreadState的transact()方法
并传入handle
句柄值,IPCThreadState
再去执行具体的Binder
命令。- 由
Binder驱动
到Server
的大概流程就是:Server
通过IPCThreadState
接收到Client
的请求后,层层向上,最后回调到Stub的onTransact()方法
。
Client
通过ServiceManager
或AMS
获取到的远程Binder
实体,一般会用Proxy
做一层封装,比如ServiceManagerProxy
、AIDL
生成的Proxy
类。而被封装的远程Binder
实体是一个BinderProxy
。
当然这不代表所有的IPC流程,比如ServiceManager作为一个Server时,便没有上层的封装,也没有借助IPCThreadState,而是初始化后通过binder_loop()方法
直接与Binder驱动
通信的。
4 Binder四层结构
Binder在整个Android系统中有这举足轻重的地位,在Native层有一套完整的binder通信的C/S架构图中红色代表整个framework层binder架构相关组件,Binder类代表Server端,BinderProxy类代码Client端
;图中的蓝色代表Native层,BpBinder作为客户端,BBinder作为服务端。
基于naive层的Binder框架,Java也有一套镜像功能的binder C/S架构,通过JNI技术与native层的binder对应,Java层的binder功能最终都是交给native的binder来完成。从kernel
到native
,jni
,framework
层的架构所涉及的所有有关类和方法见Binder类图。
上图为整个 Binder 从 kernel 至,native,JNI,Framework 层所涉及的全部类
4.1 Binder Framework层
binder在framework层,采用JNI技术来调用native(C/C++)层的binder架构,从而为上层应用程序提供服务。我们知道native层中,binder是C/S架构,分为Bn端(Server)
和Bp端(Client)
。对于java层在命名与架构上非常相近,同样实现了一套IPC通信架构。
4.1.1 Stub与Proxy机制
Stub为服务端,接收数据。
Proxy为客户端,发送数据。
4.1.2 ServiceManager Java对象如何管理服务
ServiceManager是Binder机制的大管家,管理着android系统的各种Service。Service向ServiceManager注册,当Client需要调用Service时,先通过ServiceManager查询到该Service,Client接着再与Service通信。这些Service有java层也有native层的。native层通过BpServiceManager/BnServiceManager实现的Service与ServiceManager的交互。
4.1.3 Client端和Service端沟通机制
同native层类似,java层,aidl脚本依据IServiceManager.aidl生成IServiceManager接口类,包含子类IServiceManager.Stub
和IServiceManager.Stub.Proxy
,两者都实现了IServiceManager接口,前者代表server端
,后者代表client端
。
public interface IServiceManager extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements android.os.IServiceManager
{
public static android.os.IServiceManager asInterface(android.os.IBinder obj)
{
if (obj == null){
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IServiceManager){
return (IServiceManager)iin;
}
return new IServiceManager.Stub.Proxy(obj);
}
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
{
String descriptor = DESCRIPTOR;
...
switch (code){
case TRANSACTION_getService:
{
String _arg0;
_arg0 = data.readString();
data.enforceNoDataAvail();
IBinder _result = getService(_agr0);
reply.writeNoException();
reply.writeStrongBinder(_result);
break;
}
...
}
}
}
private static class Proxy implements IServiceManager
{
private IBinder mRemote;
Proxy(IBinder remote){
mRemote = remote;
}
@Override
public IBinder getService(String name) throws RemoteException
{
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
IBinder _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_getService, _data, _reply, 0);
_reply.readException();
_result = _reply.readStrongBinder();
}finally{
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public IBinder checkService(String name) throws RemoteException
{
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
IBinder _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_checkService, _data, _reply, 0);
_reply.readException();
_result = _reply.readStrongBinder();
}finally{
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
java层使用ServiceManager类实现servicemanager的client端,通过getIServiceManager()获取servicemanager的代理类,其中BinderInternal.getContextObject()
通过jni创建一个BinderProxy对象。
这里BinderProxy本来就是java类,为什么要通过jni创建呢?目的是创建BinderPrxoy对象的同时,也创建一个BpBinder对象,BinderProxy和BpBinder是一一对应的关系;同理,创建java层Binder对象时,也会通过jni创建一个BBinder对象。我们可以理解为BinderProxy/Binder
封装了BpBinder/BBinder
,实际工作的是后者。
public final class ServiceManager {
private static IServiceManager sServiceManager;
private static IServiceManager getIServiceManager(){
if (sServiceManager != null){
return sServiceManager;
}
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocing(BinderInternal.getContextObject()));
return sServiceManager;
}
public static IBinder getService(String name){
try {
IBinder service = sCache.get(name);
if (service != null){
return service;
} else {
return Binder.allowBlocking(rawGetServices(name));
}
} catch (RemoteException e){
...
}
return null;
}
private static IBinder rawGetService(String name) throws RemoteException {
final IBinder binder = getIServiceManager().getService(name);
...
return binder;
}
}
此时,获取到的servicemanager的代理类IServiceManagerProxy(BinderProxy)
。
public final class ServiceManagerNative {
private ServiceManagerNative(){}
public static IServiceManager asInterface(IBinder obj){
if (obj == null){
return null;
}
return new ServiceManagerProxy(obj);
}
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote){
mRemote = remote;
//servicemanager实际的代理类
mServiceManager = IServiceManager.Stub.asInterface(remote);
}
public IBinder getService(String name) throws RemoteException {
return mServiceManager.checkService(name);
}
...
private IBinder mRemote;
private IServiceManager mServiceManager;
}
}
这样,我们使用ServiceManager.addService()/getService()
等等方法时,会走到BinderProxy
的transact()->transactNative()
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
...
try {
return transactNative(code, data, reply, flags);
}finally{
...
}
}
public native boolean transactNative(int code, Parcel data, Parecl reply, int flags) throws RemoteException;
最终通过jni进入native层,通过BpBinder.transact()->IPCThreadState.transact()->writeTransactionData()->waitForResponse()
进入binder驱动层,与servicemanager进程通信,等待返回结果。
注意,这里的servicemanager的服务端,使用的是native层的BnServiceManager
,并没有使用到java层的IServiceManager.Stub
servicemanager接收到请求后,执行相应的操作IPCThreadState::executeCommand()->BBinder::transact()->BnServiceManager::onTransact()
, 具体的实现方法在ServiceManager.cpp中,这里不详细开展了。
::android::status_t BnServiceManager::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags){
::android::status_t _aidl_ret_status = ::android::OK;
switch(_aidl_code){
case BnServiceManager::TRANSACTION_getService:
::std::string in_name;
::android::sp<::android::IBinder> _aidl_return;
...
_aidl_ret_status = _aidl_data.readUtf8FromUtf16(&in_name);
...
::android::binder::Status _aidl_status(getService(in_name, &_aidl_return));
...
_aidl_ret_status = _aidl_reply->writeStrongBinder(_aidl_return);
break;
case BnServiceManager::TRANSACTION_checkService:
...
}
}
数据组装完毕后,回到IPCThreadState::executeCommand()
,执行sendReply()
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags){
status_t err;
status_t statusBuffer;
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
if (err < NO_ERROR) return err;
return waitForResponse(nullptr, nullptr);
}
通过binder驱动,将请求的结果返回给client端,如下所示,返回的结果写入了Parcel中。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
...
while(1){
if ((err=talkWithDriver()) < NO_ERROR) break;
...
cmd = (uint32_t)mIn.readInt32();
switch(cmd){
...
case BR_REPLY:
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
...
if(reply){
if((tr.flags & TF_STATUS_CODE) == 0){
reply->ipcSetDataReference(reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), tr.data_size, reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
}else{
...
}
}else{
...
}
goto finish;
}
}
finish:
if (err != NO_ERROR){
...
}
return err;
}
接着进入IServiceManager.Stub.Proxy
类的方法中,通过Parcel.readStrongBinder()
获取到与servicemanager通信的结果。
总结:servicemanager在管理java层service时,目前只使用了IServiceManager.Stub.Proxy
作为代理类,并没有使用IServiceManager.Stub
作为服务类,服务类使用的依然是native层BnServiceManager
。
4.2 Binder jni层
4.2.1 android_util_binder jni 接口如何实现 native 方法
服务注册
int register_android_os_Binder(JNIEnv* env)
{
if (int_register_android_os_Binder(env) < 0)
return -1;
if (int_register_android_os_BinderInternal(env) < 0)
return -1;
if (int_register_android_os_BinderProxy(env) < 0)
return -1;
jclass clazz = FindClassOrDie(env, "android/util/Log");
gLogOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gLogOffsets.mLogE = GetStaticMethodIDOrDie(env, clazz, "e",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I");
clazz = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
gParcelFileDescriptorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gParcelFileDescriptorOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>",
"(Ljava/io/FileDescriptor;)V");
clazz = FindClassOrDie(env, "android/os/StrictMode");
gStrictModeCallbackOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gStrictModeCallbackOffsets.mCallback = GetStaticMethodIDOrDie(env, clazz,
"onBinderStrictModePolicyChange", "(I)V");
return 0;
}
static int int_register_android_os_Binder(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, kBinderPathName);
gBinderOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
return RegisterMethodsOrDie(
env, kBinderPathName,
gBinderMethods, NELEM(gBinderMethods));
}
static int int_register_android_os_BinderInternal(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, kBinderInternalPathName);
gBinderInternalOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderInternalOffsets.mForceGc = GetStaticMethodIDOrDie(env, clazz, "forceBinderGc", "()V");
return RegisterMethodsOrDie(
env, kBinderInternalPathName,
gBinderInternalMethods, NELEM(gBinderInternalMethods));
}
static int int_register_android_os_BinderProxy(JNIEnv* env)
{
jclass clazz = FindClassOrDie(env, "java/lang/Error");
gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
clazz = FindClassOrDie(env, kBinderProxyPathName);
gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);
gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
"(Landroid/os/IBinder$DeathRecipient;)V");
gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
gBinderProxyOffsets.mSelf = GetFieldIDOrDie(env, clazz, "mSelf",
"Ljava/lang/ref/WeakReference;");
gBinderProxyOffsets.mOrgue = GetFieldIDOrDie(env, clazz, "mOrgue", "J");
clazz = FindClassOrDie(env, "java/lang/Class");
gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
return RegisterMethodsOrDie(
env, kBinderProxyPathName,
gBinderProxyMethods, NELEM(gBinderProxyMethods));
}
int_register_android_os_Binder
方法的主要功能:
通过gBinderOffsets
,保存Java层Binder类的信息,为JNI层访问Java层提供通道;
通过RegisterMethodsOrDie
,将gBinderMethods数组
完成映射关系,为Java层访问JNI层提供通道。
也就是说该过程建立了Binder类在Native层与framework层之间的相互调用的
桥梁。
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
BinderInternal.java中有一个native方法getContextObject()
,JNI调用执行上述方法。ProcessState::self()->getContextObject()
等价于newBpBinder(0)
。在ProcessState的Self中,会调用自身的构造函数,在构造函数中,会做几件事情:
- 打开binder设备,设置服务的
最大线程数目为15个
- 用mmap做内存映射(大小为
1M-8K
)
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
if (val == NULL) return NULL;
if (val->checkSubclass(&gBinderOffsets)) {
// One of our own!
jobject object = static_cast<JavaBBinder*>(val.get())->object();
LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
// For the rest of the function we will hold this lock, to serialize
// looking/creation/destruction of Java proxies for native Binder proxies.
AutoMutex _l(mProxyLock);
// Someone else's... do we know about it?
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = jniGetReferent(env, object);
if (res != NULL) {
ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
return res;
}
LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
// The proxy holds a reference to the native object.
env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
val->incStrong((void*)javaObjectForIBinder);
// The native object needs to hold a weak reference back to the
// proxy, so we can retrieve the same proxy if it is still active.
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Also remember the death recipients registered on this proxy
sp<DeathRecipientList> drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
incRefsCreated(env);
}
return object;
}
根据BpBinder(C++)生成BinderProxy(Java)对象.主要工作是创建BinderProxy对象,并把BpBinder对象地址保存到BinderProxy.mObject成员变量。到此可以知ServiceManagerNative.asInterface(BinderInternal.getContextObject())等价于ServiceManagerNative.asInterface(newBinderProxy())
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
if (obj == NULL) return NULL;
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
return jbh != NULL ? jbh->get(env, obj) : NULL;
}
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
return (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
}
ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
根据Binde(Java)生成JavaBBinderHolder(C++)对象.主要工作是创建JavaBBinderHolder对象,并把JavaBBinderHolder对象地址保存到Binder.mObject成员变量
class JavaBBinderHolder : public RefBase
{
public:
sp<JavaBBinder> get(JNIEnv* env, jobject obj)
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, obj);
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
}
return b;
}
sp<JavaBBinder> getExisting()
{
AutoMutex _l(mLock);
return mBinder.promote();
}
private:
Mutex mLock;
wp<JavaBBinder> mBinder;
};
JavaBBinderHolder有一个成员变量mBinder,保存当前创建的JavaBBinder对象,这是一个wp类型的,可能会被垃圾回收器给回收,所以每次使用前,都需要先判断是否存在。
JavaBBinder(JNIEnv* env, jobject object)
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
{
ALOGV("Creating JavaBBinder %p\n", this);
android_atomic_inc(&gNumLocalRefs);
incRefsCreated(env);
}
创建JavaBBinder
,该对象继承于BBinder
对象。data.writeStrongBinder(service)
最终等价于parcel->writeStrongBinder(newJavaBBinder(env,obj));
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
if (dataObj == NULL) {
jniThrowNullPointerException(env, NULL);
return JNI_FALSE;
}
Parcel* data = parcelForJavaObject(env, dataObj);
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj);
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
IBinder* target = (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n",
target, obj, code);
bool time_binder_calls;
int64_t start_millis;
if (kEnableBinderSample) {
// Only log the binder call duration for things on the Java-level main thread.
// But if we don't
time_binder_calls = should_time_binder_calls();
if (time_binder_calls) {
start_millis = uptimeMillis();
}
}
//printf("Transact from Java code to %p sending: ", target); data->print();
status_t err = target->transact(code, *data, reply, flags);
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
if (kEnableBinderSample) {
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
}
}
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/, data->dataSize());
return JNI_FALSE;
}
Java层的BinderProxy.transact()
最终交由Native层的BpBinder::transact()
完成。NativeBinder的注册服务(addService)中有详细说明BpBinder执行过程。另外,该方法可抛出RemoteException。
4.2.2 AndroidRuntime jni 调度机制
4.3 Binder native层
4.3.1 IPC与RPC
IPC (Inter-Process Communication)即进程间通信,是指进程间数据交互的过程。Android底层是基于Linux,而Linux基于安全考虑,是不允许两个进程间直接操作对方的数据,这就是进程隔离。
RPC(Remote Procedure Call)即远程过程调用,它是一种通过网络从远程计算机程序上请求服务,在不需要了解底层网络技术的协议下,即可获取计算机进程中的数据。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。简而言之:客户端能向服务端发送若干个进程请求,服务端根据发送的进程参数依次返回对应的计算结果。RPC可以说客户端调用服务端的接口的过程,是面向接口的编程。
Android 利用远程过程调用 (RPC) 提供了一种进程间通信 (IPC) 机制,通过这种机制,由 Activity 或其他应用组件调用的方法将(在其他进程中)远程执行,而所有结果将返回给调用方。 这就要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。 然后,返回值将沿相反方向传输回来。 Android 提供了执行这些 IPC 事务所需的全部代码,因此您只需集中精力定义和实现 RPC 编程接口即可。要执行 IPC,必须使用 bindService() 将应用绑定到服务上。也就是说,RPC在的Android具体体现,是依赖 bindService()
的方式,在onBind
方法将服务端的计算结果返回给客户端(Activity等组件)的过程。
4.3.2 service_manager服务如何随系统启动而启动
ServiceManager进程是在init进程创建的,所以我们从init进程的main()开始分析:
// 文件路径: system/core/init/main.cpp
int main(int argc, char** argv) {
...
if (!strcmp(argv[1], "second_stage")) { //TODO 根据条件会走到这个分支
return SecondStageMain(argc, argv);
}
}
int SecondStageMain(int argc, char** argv) {
...
//用来存放解析出的内容
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
//在这个方法中会对 /system/core/rootdir/init.rc 脚本文件文件进行解析
LoadBootScripts(am, sm);
//循环处理init.rc脚本中的command命令,处理完就进入等待
while (true) {
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running()))
{
//内部遍历执行每个action中携带的command对应的执行函数
am.ExecuteOneCommand();
}
}
}
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
//创建解析器
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
//解析init.rc ,这个是手机设备上的路径,和源码中system/core/init/init.rc是一个文件
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
下面是init.rc中启动servicemanager进程相关的部分:
on init
# Start essential services.
start servicemanager #启动servicemanager进程
start hwservicemanager
start vndservicemanager
有关servicemanager进程启动的细节配置被放在了frameworks\native\cmds\servicemanager\servicemanager.rc
#此脚本文件描述了启动servicemanager进程时的一些细节
#service用于通知init进程创建名为servicemanager的进程,这个进程执行程序的路径是/system/bin/servicemanager
#在手机系统中是能找到这个文件的
service servicemanager /system/bin/servicemanager
class core animation
#表明此进程是以system身份运行的
user system
group system readproc
#说明servicemanager是系统中的关键服务,关键服务是不会退出的,若退出系统则会重启,系统重启则会重启
#以下onrestart修饰的进程,也可以说明这些进程是依赖于servicemanager进程的
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
onrestart restart keystore
onrestart restart gatekeeperd
onrestart restart thermalservice
writepid /dev/cpuset/system-background/tasks
shutdown critical
总结:ServiceManager是一个独立的进程,由init进程创建
,且在创建zygote进程之前被创建
。
4.3.3 loop循环(ServiceManager的main方法)
ServiceManager的main方法:
int main(int argc, char** argv)
{
struct binder_state *bs;
union selinux_callback cb;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
// 打开 /dev/binder 初始化系统 其内部通过 mmap() 函数创建一块内存空间
// 参数 mapsize = 128*1024 就是空间的大小 128k
bs = binder_open(driver, 128*1024);
if (!bs) {
return -1;
}
// 调用了 binder.c 中的 binder_become_context_manager(bs) 函数
// 作用:把本进程设置为 Binder 框架的管理进程
// 其内部非常简单 就是通过 ioctl 将 BINDER_SET_CONTEXT_MGR 发送到了驱动
// 调用了 binder_become_context_manager 后就代表我已经准备就绪了
if (binder_become_context_manager(bs)) {
return -1;
}
cb.func_audit = audit_callback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
cb.func_log = selinux_log_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
#ifdef VENDORSERVICEMANAGER
sehandle = selinufx_android_vendor_service_context_handle();
#else
sehandle = selinux_android_service_context_handle();
#endif
selinux_status_open(true);
if (sehandle == NULL) {
ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
abort();
}
if (getcon(&service_manager_context) != 0) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
// 最后调用 binder_loop 开启消息循环。将 svcmgr_handler 函数指针传入。
// binder_loop() 函数的主要作用就是从驱动读取命令解析后再 用svcmgr_handler 处理
binder_loop(bs, svcmgr_handler);
return 0;
}
SeviceManager进程启动过程中,主要做了以下四件事:
1)初始化binder驱动,并完成映射
2)将自身以“manager” 添加到servicemanager中的map集合中
3)binder_become_context_manager,注册成为为binder驱动的上下文管理者
4)binder_loop,循环等待客户端的请求,(给Looper设置callback,进入无限循环,处理client端发来的请求)
4.3.4 内核空间与用户空间原理
在Linux中,操作系统和驱动程序运行在Kernel space(内核空间),应用程序运行在User space(用户空间)
。两者不能简单的使用指针传递数据,因为Linux使用的虚拟内核机制,当内核空间使用用户空间指针时,对应的数据可能不在内存中(数据已被换出)。用户空间的内存采用段页式,内核空间也有自己的规则。
简单说,Kernel space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不受影响。Kernel space 可以执行任意命令,调用系统的一切资源;User space 只能执行简单的运算,不能直接调用系统资源,必须通过系统接口(又称 system call),才能向内核发出指令。
4.3.5 misc_register
misc_register函数
使用来注册
对外提供操作的函数
驱动文件是一种特殊的文件,例如open 一个文件,只是打开而已。 在Linux机制中,如果操作一个驱动文件,会调用驱动对应的方法。 open ( ' / dev / binder " )
文件,会执行驱动源码的 binder_open
方法
4.4 Binder驱动层
4.4.1 基础
handle 回调函数详解
数据结构:binder_node、binder_ref、binder_proc、binder_thread
4.4.2 数据交互
在BinderIPC通信过程中,进程间通信都要先通过向Binder驱动发送BC_XXX命令,然后Binder 驱动稍做处理后通过对应的BR_XXX将命令转给给目标进程。
如果有返回值,进程也是先将返回结果以BC_REPLY的形式先发给Binder驱动,然后通过驱动以BR_REPLY命令转发。
PS:从Driver发出的命令以BR开始,而发往Driver的命令以BC开头
4.4.3 三大逻辑流程
服务注册、服务发现、服务调用,下面我们来看看Binder在c层的三大逻辑:
服务注册检测及服务发现
service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
//ALOGI("target=%p code=%d pid=%d uid=%d\n",
// (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);
if (txn->target.ptr != BINDER_SERVICE_MANAGER)
return -1;
if (txn->code == PING_TRANSACTION)
return 0;
// Equivalent to Parcel::enforceInterface(), reading the RPC
// header with the strict mode policy mask and the interface name.
// Note that we ignore the strict_policy and don't propagate it
// further (since we do no outbound RPCs anyway).
strict_policy = bio_get_uint32(msg);
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
if ((len != (sizeof(svcmgr_id) / 2)) ||
memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
fprintf(stderr,"invalid id %s\n", str8(s, len));
return -1;
}
if (sehandle && selinux_status_updated() > 0) {
struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
if (tmp_sehandle) {
selabel_close(sehandle);
sehandle = tmp_sehandle;
}
}
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//服务名
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//根据名称查找相应服务
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE:
//服务名
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
handle = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
//注册指定服务
if (do_add_service(bs, s, len, handle, txn->sender_euid,
allow_isolated, txn->sender_pid))
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
uint32_t n = bio_get_uint32(msg);
if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {
ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
txn->sender_euid);
return -1;
}
si = svclist;
while ((n-- > 0) && si)
si = si->next;
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
}
struct svcinfo
{
struct svcinfo *next;
//服务的 handle 值
uint32_t handle;
struct binder_death death;
int allow_isolated;
//名字长度
size_t len;
//服务名
uint16_t name[0];
};
该方法的功能:注册服务、查询服务,以及列举所有服务。
每一个服务用 svcinfo 结构体来表示,该 handle 值是在注册服务的过程中,由
服务所在进程那一端所确定的。
注册服务
service_manager.c
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
uint32_t handle, uid_t uid, int allow_isolated,
pid_t spid)
{
struct svcinfo *si;
//ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
// allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
if (!handle || (len == 0) || (len > 127))
return -1;
//权限检查
if (!svc_can_register(s, len, spid, uid)) {
ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
str8(s, len), handle, uid);
return -1;
}
//服务检索
si = find_svc(s, len);
if (si) {
if (si->handle) {
ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
str8(s, len), handle, uid);
//服务已注册时,释放相应的服务
svcinfo_death(bs, si);
}
si->handle = handle;
} else {
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) {
//内存不足,无法分配足够内存
ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
str8(s, len), handle, uid);
return -1;
}
si->handle = handle;
si->len = len;
//内存拷贝服务信息
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
si->name[len] = '\0';
si->death.func = (void*) svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
// svclist 保存所有已注册的服务
si->next = svclist;
svclist = si;
}
//以 BC_ACQUIRE 命令,handle 为目标的信息,通过 ioctl 发送给 binder 驱动
binder_acquire(bs, handle);
//以 BC_REQUEST_DEATH_NOTIFICATION 命令的信息,通过 ioctl 发送给 binder 驱动,
//主要用于清理内存等收尾工作。
binder_link_to_death(bs, handle, &si->death);
return 0;
}
static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, uid_t uid)
{
const char *perm = "add";
if (multiuser_get_app_id(uid) >= AID_APP) {
return 0; /* Don't allow apps to register services */
}
//检查 selinux 权限是否满足
return check_mac_perms_from_lookup(spid, uid, perm, str8(name, name_len)) ? 1 : 0;
}
void svcinfo_death(struct binder_state *bs, void *ptr)
{
struct svcinfo *si = (struct svcinfo* ) ptr;
ALOGI("service '%s' died\n", str8(si->name, si->len));
if (si->handle) {
binder_release(bs, si->handle);
si->handle = 0;
}
}
binder.c
uint32_t bio_get_ref(struct binder_io *bio)
{
struct flat_binder_object *obj;
obj = _bio_get_obj(bio);
if (!obj)
return 0;
if (obj->type == BINDER_TYPE_HANDLE)
return obj->handle;
return 0;
}
void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death)
{
struct {
uint32_t cmd;
struct binder_handle_cookie payload;
} __attribute__((packed)) data;
data.cmd = BC_REQUEST_DEATH_NOTIFICATION;
data.payload.handle = target;
data.payload.cookie = (uintptr_t) death;
binder_write(bs, &data, sizeof(data));
}
服务注册:
注册服务的分以下 3 部分工作:
svc_can_register:检查权限,检查 selinux 权限是否满足;
find_svc:服务检索,根据服务名来查询匹配的服务;
svcinfo_death:释放服务,当查询到已存在同名的服务,则先清理该服
务信息,再将当前的服务加入到服务列表 svclist;
binder_write 进入 Binder driver 后,直接调用后进入 binder_thread_write, 处理 BC_REQUEST_DEATH_NOTIFICATION 命令
服务发现
service_manager.c
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
struct svcinfo *si = find_svc(s, len);
if (!si || !si->handle) {
return 0;
}
if (!si->allow_isolated) {
// If this service doesn't allow access from isolated processes,
// then check the uid to see if it is isolated.
uid_t appid = uid % AID_USER;
if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
return 0;
}
}
if (!svc_can_find(s, len, spid, uid)) {
return 0;
}
return si->handle;
}
struct svcinfo *find_svc(const uint16_t *s16, size_t len)
{
struct svcinfo *si;
for (si = svclist; si; si = si->next) {
if ((len == si->len) &&
!memcmp(s16, si->name, len * sizeof(uint16_t))) {
return si;
}
}
return NULL;
}
服务发现:查询到目标服务,并返回该服务所对应的 handle
从 svclist 服务列表中,根据服务名遍历查找是否已经注册。当服务已存在
svclist,则返回相应的服务名,否则返回 NULL。
当找到服务的 handle, 则调用 bio_put_ref(reply, handle),将 handle 封装到
reply.
binder.c
void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
struct flat_binder_object *obj;
if (handle)
obj = bio_alloc_obj(bio);
else
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
}
void bio_put_obj(struct binder_io *bio, void *ptr)
{
struct flat_binder_object *obj;
obj = bio_alloc_obj(bio);
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->type = BINDER_TYPE_BINDER;
obj->binder = (uintptr_t)ptr;
obj->cookie = 0;
}
static void *bio_alloc(struct binder_io *bio, size_t size)
{
size = (size + 3) & (~3);
if (size > bio->data_avail) {
bio->flags |= BIO_F_OVERFLOW;
return NULL;
} else {
void *ptr = bio->data;
bio->data += size;
bio->data_avail -= size;
return ptr;
}
}
服务调用
上层通过checkService(),getService()获取到服务,实际上是调用底层的transact()方法,对其方法进行调用。
BpBinder.cpp
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
Binder 代理类调用 transact()方法,真正工作还是交给 IPCThreadState 来进行
transact 工作,
IPCThreadState.cpp
static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
static bool gHaveTLS = false;
static pthread_key_t gTLS = 0;
static bool gShutdown = false;
static bool gDisableBackgroundScheduling = false;
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState;
}
if (gShutdown) return NULL;
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
if (pthread_key_create(&gTLS, threadDestructor) != 0) {
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mMyThreadId(androidGetTid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}
TLS 是指 Thread local storage(线程本地储存空间),每个线程都拥有自己的TLS,并且是私有空间,线程之间不会共享。通过pthread_getspecific/pthread_setspecific 函数可以获取/设置这些空间中的内容。从线程本地存储空间中获得保存在其中的 IPCThreadState 对象。
每个线程都有一个 IPCThreadState,每个 IPCThreadState 中都有一个 mIn、一个mOut。成员变量 mProcess 保存了 ProcessState 变量(每个进程只有一个)。
mIn 用来接收来自 Binder 设备的数据,默认大小为 256 字节;
mOut 用来存储发往 Binder 设备的数据,默认大小为 256 字节
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
//数据错误检查
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
<< handle << " / code " << TypeCode(code) << ": "
<< indent << data << dedent << endl;
}
if (err == NO_ERROR) {
LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
(flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
// 传输数据
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
#if 0
if (code == 4) { // relayout
ALOGI(">>>>>> CALLING transaction 4");
} else {
ALOGI(">>>>>> CALLING transaction %d", code);
}
#endif
if (reply) {
//等待响应
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
#if 0
if (code == 4) { // relayout
ALOGI("<<<<<< RETURNING transaction 4");
} else {
ALOGI("<<<<<< RETURNING transaction %d", code);
}
#endif
IF_LOG_TRANSACTIONS() {
TextOutput::Bundle _b(alog);
alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
<< handle << ": ";
if (reply) alog << indent << *reply << dedent << endl;
else alog << "(none requested)" << endl;
}
} else {
//不需要响应消息的 binder 则进入该分支
err = waitForResponse(NULL, NULL);
}
return err;
}
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
// data 为记录 Media 服务信息的 Parcel 对象
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
tr.offsets_size = 0;
tr.data.ptr.offsets = 0;
} else {
return (mLastError = err);
}
//cmd = BC_TRANSACTION
mOut.writeInt32(cmd);
//写入 binder_transaction_data 数据
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing waitForResponse Command: "
<< getReturnString(cmd) << endl;
}
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;
case BR_FAILED_REPLY:
err = FAILED_TRANSACTION;
goto finish;
case BR_ACQUIRE_RESULT:
{
ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
const int32_t result = mIn.readInt32();
if (!acquireResult) continue;
*acquireResult = result ? NO_ERROR : INVALID_OPERATION;
}
goto finish;
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
freeBuffer, this);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
continue;
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
//接收数据缓冲区信息的填充。如果以后收到数据,就直接填在 mIn 中了。
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
if (outAvail != 0) {
alog << "Sending commands to driver: " << indent;
const void* cmds = (const void*)bwr.write_buffer;
const void* end = ((const uint8_t*)cmds)+bwr.write_size;
alog << HexDump(cmds, bwr.write_size) << endl;
while (cmds < end) cmds = printCommand(alog, cmds);
alog << dedent;
}
alog << "Size of receive buffer: " << bwr.read_size
<< ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
}
// Return immediately if there is nothing to do.
//当读缓冲和写缓冲都为空,则直接返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
IF_LOG_COMMANDS() {
alog << "About to read/write, write size = " << mOut.dataSize() << endl;
}
#if defined(HAVE_ANDROID_OS)
//通过 ioctl 不停的读写操作,跟 Binder Driver 进行通信
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
//当被中断,则继续执行
} while (err == -EINTR);
IF_LOG_COMMANDS() {
alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
<< bwr.write_consumed << " (of " << mOut.dataSize()
<< "), read consumed: " << bwr.read_consumed << endl;
}
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
IF_LOG_COMMANDS() {
TextOutput::Bundle _b(alog);
alog << "Remaining data size: " << mOut.dataSize() << endl;
alog << "Received commands from driver: " << indent;
const void* cmds = mIn.data();
const void* end = mIn.data() + mIn.dataSize();
alog << HexDump(cmds, mIn.dataSize()) << endl;
while (cmds < end) cmds = printReturnCommand(alog, cmds);
alog << dedent;
}
return NO_ERROR;
}
return err;
}
其中 handle 的值用来标识目的端,注册服务过程的目的端为 service manager,此处 handle=0 所对应的是 binder_context_mgr_node 对象,正是 servicemanager 所对应的 binder 实体对象。binder_transaction_data 结构体是 binder驱动通信的数据结构,该过程最终是把 Binder 请求码 BC_TRANSACTION 和binder_transaction_data 结构体写入到 mOut。
binder_write_read 结构体用来与 Binder 设备交换数据的结构, 通过 ioctl 与mDriverFD 通信,是真正与 Binder 驱动进行数据读写交互的过程。先向 servicemanager 进程发送查询服务的请求(BR_TRANSACTION)。
当 service manager 进程收到该命令后,会执行do_find_service() 查询服务所对应的 handle,然后再 binder_send_reply()应答发起者,发送 BC_REPLY 协议,然后调用 binder_transaction(),再向服务请求者的 Todo 队列 插入事务。
接下来,再看看 binder_transaction 过程。
binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
//根据各种判定,获取以下信息:
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t *offp, *off_end;
//目标进程
struct binder_proc *target_proc;
//目标线程
struct binder_thread *target_thread = NULL;
//目标binder节点
struct binder_node *target_node = NULL;
//目标TODO队列
struct list_head *target_list;
//目标等待队列
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error;
e = binder_transaction_log_add(&binder_transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
e->from_proc = proc->pid;
e->from_thread = thread->pid;
e->target_handle = tr->target.handle;
e->data_size = tr->data_size;
e->offsets_size = tr->offsets_size;
if (reply) {
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
binder_user_error("%d:%d got reply transaction with no transaction stack\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_empty_call_stack;
}
binder_set_nice(in_reply_to->saved_priority);
if (in_reply_to->to_thread != thread) {
binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, in_reply_to->debug_id,
in_reply_to->to_proc ?
in_reply_to->to_proc->pid : 0,
in_reply_to->to_thread ?
in_reply_to->to_thread->pid : 0);
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
goto err_bad_call_stack;
}
thread->transaction_stack = in_reply_to->to_parent;
target_thread = in_reply_to->from;
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
if (target_thread->transaction_stack != in_reply_to) {
binder_user_error("%d:%d got reply transaction with bad target transaction stack %d, expected %d\n",
proc->pid, thread->pid,
target_thread->transaction_stack ?
target_thread->transaction_stack->debug_id : 0,
in_reply_to->debug_id);
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
target_proc = target_thread->proc;
} else {
if (tr->target.handle) {
struct binder_ref *ref;
ref = binder_get_ref(proc, tr->target.handle);
if (ref == NULL) {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_invalid_target_handle;
}
target_node = ref->node;
} else {
target_node = binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
}
}
e->to_node = target_node->debug_id;
target_proc = target_node->proc;
if (target_proc == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
if (tmp->to_thread != thread) {
binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, tmp->debug_id,
tmp->to_proc ? tmp->to_proc->pid : 0,
tmp->to_thread ?
tmp->to_thread->pid : 0);
return_error = BR_FAILED_REPLY;
goto err_bad_call_stack;
}
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
if (target_thread) {
e->to_thread = target_thread->pid;
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else {
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
}
e->to_proc = target_proc->pid;
/* TODO: reuse incoming transaction for reply */
//分配两个结构体内存
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
//分配两个结构体内存
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
t->debug_id = ++binder_last_id;
e->debug_id = t->debug_id;
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
"%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_thread->pid,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
(u64)tr->data_size, (u64)tr->offsets_size);
else
binder_debug(BINDER_DEBUG_TRANSACTION,
"%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_node->debug_id,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
(u64)tr->data_size, (u64)tr->offsets_size);
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
trace_binder_transaction(reply, t, target_node);
//从target_proc分配一块buffer
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
offp = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n",
proc->pid, thread->pid, (u64)tr->offsets_size);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
off_end = (void *)offp + tr->offsets_size;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
if (*offp > t->buffer->data_size - sizeof(*fp) ||
t->buffer->data_size < sizeof(*fp) ||
!IS_ALIGNED(*offp, sizeof(u32))) {
binder_user_error("%d:%d got transaction with invalid offset, %lld\n",
proc->pid, thread->pid, (u64)*offp);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
switch (fp->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_new_node_failed;
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
if (fp->cookie != node->cookie) {
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid,
(u64)fp->binder, node->debug_id,
(u64)fp->cookie, (u64)node->cookie);
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
ref = binder_get_ref_for_node(target_proc, node);
if (ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->handle = ref->desc;
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
trace_binder_transaction_node_to_ref(t, node, ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx -> ref %d desc %d\n",
node->debug_id, (u64)node->ptr,
ref->debug_id, ref->desc);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
if (ref == NULL) {
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
proc->pid,
thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
if (ref->node->proc == target_proc) {
if (fp->type == BINDER_TYPE_HANDLE)
fp->type = BINDER_TYPE_BINDER;
else
fp->type = BINDER_TYPE_WEAK_BINDER;
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
trace_binder_transaction_ref_to_node(t, ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> node %d u%016llx\n",
ref->debug_id, ref->desc, ref->node->debug_id,
(u64)ref->node->ptr);
} else {
struct binder_ref *new_ref;
new_ref = binder_get_ref_for_node(target_proc, ref->node);
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
fp->handle = new_ref->desc;
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
trace_binder_transaction_ref_to_ref(t, ref,
new_ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, new_ref->debug_id,
new_ref->desc, ref->node->debug_id);
}
} break;
case BINDER_TYPE_FD: {
int target_fd;
struct file *file;
if (reply) {
if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
proc->pid, thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_fd_not_allowed;
}
} else if (!target_node->accept_fds) {
binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
proc->pid, thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_fd_not_allowed;
}
file = fget(fp->handle);
if (file == NULL) {
binder_user_error("%d:%d got transaction with invalid fd, %d\n",
proc->pid, thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_fget_failed;
}
target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
if (target_fd < 0) {
fput(file);
return_error = BR_FAILED_REPLY;
goto err_get_unused_fd_failed;
}
task_fd_install(target_proc, target_fd, file);
trace_binder_transaction_fd(t, fp->handle, target_fd);
binder_debug(BINDER_DEBUG_TRANSACTION,
" fd %d -> %d\n", fp->handle, target_fd);
/* TODO: fput? */
fp->handle = target_fd;
} break;
default:
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
proc->pid, thread->pid, fp->type);
return_error = BR_FAILED_REPLY;
goto err_bad_object_type;
}
}
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
} else {
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
if (target_node->has_async_transaction) {
target_list = &target_node->async_todo;
target_wait = NULL;
} else
target_node->has_async_transaction = 1;
}
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
wake_up_interruptible(target_wait);
return;
err_get_unused_fd_failed:
err_fget_failed:
err_fd_not_allowed:
err_binder_get_ref_for_node_failed:
err_binder_get_ref_failed:
err_binder_new_node_failed:
err_bad_object_type:
err_bad_offset:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
binder_transaction_buffer_release(target_proc, t->buffer, offp);
t->buffer->transaction = NULL;
binder_free_buf(target_proc, t->buffer);
err_binder_alloc_buf_failed:
kfree(tcomplete);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
err_alloc_tcomplete_failed:
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
err_alloc_t_failed:
err_bad_call_stack:
err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
err_no_context_mgr_node:
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
"%d:%d transaction failed %d, size %lld-%lld\n",
proc->pid, thread->pid, return_error,
(u64)tr->data_size, (u64)tr->offsets_size);
{
struct binder_transaction_log_entry *fe;
fe = binder_transaction_log_add(&binder_transaction_log_failed);
*fe = *e;
}
BUG_ON(thread->return_error != BR_OK);
if (in_reply_to) {
thread->return_error = BR_TRANSACTION_COMPLETE;
binder_send_failed_reply(in_reply_to, return_error);
} else
thread->return_error = return_error;
}
这个过程非常重要,分两种情况来说:
- 当请求服务的进程与服务属于不同进程,则为请求服务所在进程创建binder_ref 对象,指向服务进程中的 binder_node;
- 当请求服务的进程与服务属于同一进程,则不再创建新对象,只是引用计数 加 1 , 并 且 修 改 type 为 BINDER_TYPE_BINDER 或BINDER_TYPE_WEAK_BINDER。
4.4.4 数据拷贝
一次数据拷贝,发生在Client端:copy_from_user
binder.c
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
准确来说是一次拷贝分开两次操作,一次信息的拷贝(就像请求头部),第二次数据的拷贝
4.4.5 transction_task
发送端与接收端
传输流程
服务使用
4.4.6 server线程
有多个client
发送请求时server
会忙不过来,导致创建多个线程。client请求
时会将数据放至todo链表
,并且会唤醒等待wait队列的线程
,如果有线程在wait队列中等待表示server忙得过来,如果没有表示忙不过来。此时驱动会向应用程序反馈,你应该多创建一些线程来处理。
驱动向APP发出“创建新线程请求”的条件
proc->requested_threads=0, 未处理的新线程请求。
proc->ready_threads为0,空闲的线程数
proc->requested_threads_started < proc->max_threads。 已启动的线程数<max_threads
*consumed = ptr - buffer;
if (proc->requested_threads + proc->ready_threads == 0 &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++;
binder_debug(BINDER_DEBUG_THREADS,
"binder: %d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
SAMPLE_INFO("%s (%d, %d) [%s]", proc->tsk->comm, proc->pid, thread->pid, binder_cmd_name(BR_SPAWN_LOOPER));
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
}
return 0;
当创建好新线程后,新线程会执行ioctl表明已经进入了loop
binder_thread_write
case BC_REGISTER_LOOPER:
proc->requested_threads_started++;在此设置了thread_started增加线程的数量
如何创建新线程
- 设置max_threads
proc->max_threads = 0。
binder_ioctl
case BINDER_SET_MAX_THREADS
copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads) - 收到BR_SPAWN_LOOPER。
创建线程时为什么使用pthread_create()而不使用fork。因为fork会复制mmap的地址,mmap地址设置为不可复制 - 再ioctl BC_REGISTER_LOOPER表示已经正常运行了。
在驱动中会更新如下两个变量。
switch BC_REGISTER_LOOPER
proc->requested_threads–;
proc->requested_threads_started++; - 向主线一进进入循环体,读驱动、处理。
5 面试题
1.Binder 的优缺点
优点:
- 性能方面
共享内存 0 次数据拷贝
Binder 1 次数据拷贝
Socket/管道/消息队列 2 次数据拷贝
- 稳定性方面
Binder:基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,
架构清晰、职责明确又相互独立,自然稳定性更好
共享内存:虽然无需拷贝,但是控制复杂,难以使用
从稳定性的角度讲,Binder 机制是优于内存共享的。
- 安全性方面
传统的 IPC 没有任何安全措施,安全依赖上层协议来确保。
传统的 IPC 方法无法获得对方可靠的进程用户 ID/进程 UI(UID/PID),从而无法鉴
别对方身份。
传统的 IPC 只能由用户在数据包中填入 UID/PID,容易被恶意程序利用。
传统的 IPC 访问接入点是开放的,无法阻止恶意程序通过猜测接收方地址获得连接。
Binder 既支持实名 Binder,又支持匿名 Binder,安全性高。
- 从语言层面的角度
大家多知道Linux是基于C语言(面向过程的语言),而Android是基于Java语言(面向对象的语句),而对于Binder恰恰也符合面向对象的思想,将进程间通信转化为通过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。可以从一个进程传给其它进程,让大家都能访问同一Server,就像将一个对象或引用赋值给另一个引用一样。Binder模糊了进程边界,淡化了进程间通信过程,整个系统仿佛运行于同一个面向对象的程序之中。从语言层面,Binder更适合基于面向对象语言的Android系统,对于Linux系统可能会有点“水土不服”。
- 从公司战略的角度
众所周知,Linux内核是开源的系统,所开放源代码许可协议GPL保护,该协议具有“病毒式感染”的能力,怎么理解这句话呢?受GPL保护的Linux Kernel是运行在内核空间,对于上层的任何类库、服务、应用等运行在用户空间,一旦进行SysCall(系统调用),调用到底层Kernel,那么也必须遵循GPL协议。
而Android 之父 Andy Rubin对于GPL显然是不能接受的,为此,Google巧妙地将GPL协议控制在内核空间,将用户空间的协议采用Apache-2.0协议(允许基于Android的开发商不向社区反馈源码),同时在GPL协议与Apache-2.0之间的Lib库中采用BSD证授权方法,有效隔断了GPL的传染性,仍有较大争议,但至少目前缓解Android,让GPL止步于内核空间,这是Google在GPL Linux下 开源与商业化共存的一个成功典范。
缺点:
- Binder共享内存耗尽
Binder的性能(减少一次copy_to_user)和安全是最大优势,但由于Binder在内核和用户态都对传输的数据量有限制,因此要避免通过Binder传输大量数据。一个进程用于接收Binder数据的共享内存为1M-8K,对于oneway要减半。同时,Binder driver也有4M上限的限制,当多个线程共用这块共享内存时,一旦driver发现数据接收方共享内存不够,就会返回错误。
Binder共享内存耗尽的影响:
Binder调用耗时长,甚至失败;
若是用户操作的关键流程,则会导致卡顿发生。
优化建议:
及时释放data(Server端)或reply(Client端),建议用AIDL框架;
Binder接口设计时避免大数据的参数传递,若有需要可用Ashmem传递;
避免短时间内大量线程同时并行调用某Server,若有需要,需做削峰处理。
- Binder线程池耗尽
Server端有一定数量的Binder线程池来响应Client的调用,一个进程的Binder线程数默认最大是16(1个主线程和15个非主线程),超过的请求会被阻塞等待空闲的Binder线程,即Binder线程池耗尽会导致后面的调用被阻塞。
Binder线程池耗尽的影响:
同步调用的Client端被阻塞,若是用户操作的关键流程中,则发生卡顿现象;
system_server等系统关键服务进程的Binder线程池耗尽,则造成整机卡顿。
优化建议:
避免短时间内大量线程同时并行调用某Server,若有需要则需做削峰处理;
提升Binder接口实现的执行效率。
- 创建大量BpBinder或Binder对象
BpBinder是客户端中的Binder引用,保存着目标服务的handle信息,即服务端的Binder实体的引用信息,用于查询内核中的Binder节点,并同Binder实体通信。
系统并未限制Server端的Binder对象创建,理论来说可以大量创建,但一个Service应该仅创建一个Binder对象,BpBinder对象有上限6000/2500个,多了会被杀掉,因此建议同一个Service的BpBinder实现进程内共用。
创建大量BpBinder或Binder对象的影响:
内存占用,频繁GC,甚至因OOM而闪退;
整机卡顿。
优化建议:
一个Service仅一个Binder对象实例,按使用场景和生命周期合并Service;
及时释放不再使用的BpBinder。
- 使用多个ServiceConnection对象Bind同一个Service
ServiceConnection其实也是一个Service,提供给AMS维护,用于管理目标Service的回调。同一个ServiceConnection对象可以管理多个Service,Client端已做到对不同Service的复用,AMS仅维护一个IServiceConnection;但不同ServiceConnection对象没有做到复用,即使是同一个Service。
使用多个ServiceConnection对象Bind同一个Service的影响:
增加AMS的维护负担,Service的启动/退出都会持有AMS锁后遍历SC;
长时间持有AMS锁,导致整机卡顿。
优化建议:
复用同一个ServiceConnection对象,特别是同一个Service;
监听Service的死亡回调,若有需要可立即重新Bind;
及时unbind不再使用的Service。
- 随用随获取系统服务
系统服务由ServiceManager维护,获取系统服务IBinder的过程也是一次跨进程Binder调用。应用常用的系统服务在应用进程启动时就已由ATMS带过去了,而其他系统服务系统对普通应用使用hiden接口来限制。当出现频繁调checkService接口的,主要是系统服务或系统应用。
随用随获取系统服务影响:
不必要的跨进程同步调用耗时1ms左右,有些甚至达到s级(logcat -b events -s service_manager_slow);
增加系统负荷。
优化建议:
一次获取成功后本地缓存;
通过IBinder的linkToDeath机制感知到服务的退出,服务退出后清除本地IBinder缓存,下次需要时再次向Servicemanager获取。
2.Binder 是如何做到一次拷贝的(mmap如何实现一次拷贝)
主要是因为 Linux 是使用的虚拟内存寻址方式,它有如下特性:
- 用户空间的虚拟内存地址是映射到物理内存中的
- 对虚拟内存的读写实际上是对物理内存的读写,这个过程就是内存映射
- 这个内存映射过程是通过系统调用 mmap()来实现。
Binder 借助了内存映射的方法,在内核空间和接收方用户空间的数据缓存区之间做了一层内存映射,就相当于直接拷贝到了接收方用户空间的数据缓存区,从而减少了一次数据拷贝
Client与Server处于不同进程有着不同的虚拟地址规则,所以无法直接通信。
而一个页框可以映射给多个页,那么就可以将一块物理内存分别与Client和Server的虚拟内存块进行映射。
如图,Client就只需copy_from_user进行一次数据拷贝,Server进程就能读取到数据了。
另外映射的虚拟内存块大小将近**1M(1M-8K)
**,所以IPC通信传输的数据量也被限制为此值。
(注:实际上是一次拷贝分开两次操作,分别为头部信息拷贝和数据拷贝)
3.MMAP 的内存映射原理了解吗
MMAP 内存映射的实现过程,总的来说可以分为三个阶段:
(一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
- 进程在用户空间调用库函数 mmap,原型:void *mmap(void *start, size_t length, int
prot, int flags, int fd, off_t offset); - 在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址
- 为此虚拟区分配一个 vm_area_struct 结构,接着对这个结构的各个域进行了初始化
- 将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中
(二)调用内核空间的系统调用函数 mmap(不同于用户空间函数),实现文件
物理地址和进程虚拟地址的一一映射关系
- 为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找
到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文
件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。 - 通过该文件的文件结构体,链接到 file_operations 模块,调用内核函数 mmap,其原
型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。 - 内核 mmap 函数通过虚拟文件系统 inode 模块定位到文件磁盘物理地址。
- 通过 remap_pfn_range 函数建立页表,即实现了文件地址和虚拟地址区域的映射关
系。此时,这片虚拟地址并没有任何数据关联到主存中。
(三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理
内存(主存)的拷贝
注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数
据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。
进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一
段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有
拷贝到内存中,因此引发缺页异常。
- 缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。
- 调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则
调用 nopage 函数把所缺的页从磁盘装入到主存中。 - 之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时
间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。
注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调
用 msync()来强制同步, 这样所写的内容就能立即保存到文件里了。
4.Binder 机制是如何跨进程的
1.Binder 驱动
在内核空间创建一块接收缓存区,
实现地址映射:将内核缓存区、接收进程用户空间映射到同一接收缓存区
2.发送进程通过系统调用(copy_from_user)将数据发送到内核缓存区。由于内
核缓存区和接收进程用户空间存在映射关系,故相当于也发送了接收进程的用户
空间,实现了跨进程通信。
5.说说四大组件的通信机制
- activity
(1)一个 Activity 通常就是一个单独的屏幕(窗口)。
(2)Activity 之间通过 Intent 进行通信。
(3)android 应用中每一个 Activity 都必须要在 AndroidManifest.xml 配置文件中
声明,否则系统将不识别也不执行该 Activity。 - service
(1)service 用于在后台完成用户指定的操作。service 分为两种:
started(启动):当应用程序组件(如 activity)调用 startService()方法启动服务时,
服务处于 started 状态。
bound(绑定):当应用程序组件调用 bindService()方法绑定到服务时,服务处于 bound
状态。
(2)startService()与 bindService()区别:
started service(启动服务)是由其他组件调用 startService()方法启动的,这导致服务
的 onStartCommand()方法被调用。当服务是 started 状态时,其生命周期与启动它的
组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,
服务需要在完成任务后调用 stopSelf()方法停止,或者由其他组件调用 stopService()
方法停止。
使用 bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服
务也就终止,大有“不求同时生,必须同时死”的特点。
(3)开发人员需要在应用程序配置文件中声明全部的 service,使用
标签。
(4)Service 通常位于后台运行,它一般不需要与用户交互,因此 Service 组件没有
图形用户界面。Service 组件需要继承 Service 基类。Service 组件通常用于为其他
组件提供后台服务或监控其他组件的运行状态。 - content provider
(1)android 平台提供了 Content Provider 使一个应用程序的指定数据集提供给
其他应用程序。其他应用可以通过 ContentResolver 类从该内容提供者中获取或
存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录
数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数
据访问方式。
(3)ContentProvider 实现数据共享。ContentProvider 用于保存和获取数据,并
使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为
android 没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用 ContentProvider 类的对象,大多数是通过
ContentResolver 对象实现对 ContentProvider 的操作。
(5)ContentProvider 使用 URI 来唯一标识其数据集,这里的 URI 以 content://
作为前缀,表示该数据由 ContentProvider 来管理。 - broadcast receiver
(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电
话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界
面。然而,它们可以启动一个 activity 或 serice 来响应它们收到的信息,或者用
NotificationManager 来通知用户。通知可以用很多种方式来吸引用户的注意力,
例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,
用户可以打开它并获取消息。
(2)广播接收者的注册有两种方法,分别是程序动态注册和 AndroidManifest 文
件中进行静态注册。
(3)动态注册广播接收器特点是当用来注册的 Activity 关掉后,广播也就失效了。
静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也
是打开着的。也就是说哪怕 app 本身未启动,该 app 订阅的广播在触发时也会对
它起作用。
6.为什么 Intent 不能传递大数据
Intent 携带信息的大小其实是受 Binder 限制。数据以 Parcel 对象的形式存放在
Binder 传递缓存中。如果数据或返回值比传递 buffer 大,则此次传递调用失败并
抛出 TransactionTooLargeException 异常。
Binder 传递缓存有一个限定大小,通常是 1Mb。但同一个进程中所有的传输共享
缓存空间。多个地方在进行传输时,即时它们各自传输的数据不超出大小限制,
TransactionTooLargeException 异常也可能会被抛出。在使用 Intent 传递数据时,
1Mb 并不是安全上限。因为 Binder 中可能正在处理其它的传输工作。不同的机
型和系统版本,这个上限值也可能会不同。
更多推荐
所有评论(0)