Binder In Native
关于Binder的设计思想与Driver层实现细节可以看这个:Android Binder设计与实现 - 设计篇,这里首先简要概括一下。
Service的每个Binder实体位于Service所属的进程种中,Binder实体在驱动中被表示为binder_node,并通过成员refs指向了驱动中所有对这个Binder实体的引用,Binder引用在驱动被表示为binder_ref,并通过成员node指向所引用的Binder实体。
每个使用Binder的进程都会在它的ProcessState的构造函数中打开Binder设备,当打开Binder设置时会调用驱动的binder_open,在binder_open中,会为使用Binder的进程创建一个binde_proc节点,binder_proc的成员nodes索引了这个进程创建的所有Binder实体,refs_by_desc与refs_by_node则是分别以这个进程引用的Binder实体的引用号与引用的实体在内核中的内在地址为索引构建的红黑树。这样每个进程都可以通过自己的binder_proc节点检索到所有自己创建的Binder实体与所有对其他Binder实体的引用。
匿名Binder要通过实名Binder传递,而实名Binder要向ServiceManager注册。所以首先一定要有进程通过调用ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0)成为ServiceManager,当有进程申请成为ServiceManager时,驱动就通过binder_new_node建立内核中的第一个binder_node节点。
数据在驱动中以binder_transaction_date结构传输,binder_transaction_data的成员ptr.buffer指向要发送的数据的内存地址,在进程间也可以传送Binder实体或引用,比如发送匿名Binder,当Binder实体或引用在数据中传输时,就需要一个方法将Binder实体或引用在数据中的位置指出来,ptr.offsets就指向Binder偏移数组,offsets_size指明了Binder偏移数组的大小,通过这两个成员驱动就可以找到传输的数据中的所有Binder实体或引用。Binder实体或引用在传递时被表示为flat_binder_object,flat_binder_object的type域表示传输的Binder的类型,TYPE_BINDER_(WEAK)_TYPE表示传输的是Binder实体,TYPE_BINDER_(WEAK)_HANDLE表示的是Binder引用,BINDER_TYPE_FD表示文件Binder。
Client要向Service发送请求,一定要获得对Service的Binder实体的引用,Client向Service发送请求时,以引用号指明要向哪个Service发送请求,引用号0表示向ServiceManager发送请求。
一般情况下,如果Client要向某一Service进行一个请求,首先会通过向引用号为0的Binder引用发送GET_SERVICE请求获得自己需要的Service在的引用,然后再向这个引用即这个引用对应的Service发送请求。
驱动会将所有发送到引用号为0的请求转发至ServiceManager,当一个进程向0号引用即ServiceManager请求某一个Service时,ServiceManager会检测一个表查找Client请求的Service是否已向自己注册,当Binder实体向ServiceManager注册时,ServiceManager会将Binder实体的名字与引用存入一个查找表中,如果已经注册,就将Service所注册的Binder引用返回给请求的进程。
当ServiceManager将某一进程请求的Service的Binder引用发送给这一进程时,由于传送的是引用,所以flat_binder_object的type的值是TYPE_BINDER_(WEAK)_HANDLE,驱动通过binder_transaction_date的ptr.offsets和offsets_size知道了返回数据中包含Binder实体或引用,然后通过这两个成员找出数据中的Binder实体或引用,通过flat_binder_object的type成员知道了返回数据中包含的是Binder引用,然后新建一个对Service的Binder实体的引用并同时保存到Binder实体在驱动中的节点binder_node的refs成员与Client进程的binder_proc中。
Client得到了Service的引用就可以以这个引用向Service发送请求了,数据包是binder_transaction_date结构体,其成员target是一个联合,target.handle表示Client对Service的引用号,target.ptr表示Binder实体在Service进程中的内存地址,当Client向Service发送请求时填充target.handle域,驱动根据Client所属的binder_proc节点与引用号handle获得Client对Service的Binder实体的引用binder_ref,然后通过binder_ref的node成员获得Service的Binder实体在内核中的节点binder_node,然后将Client的请求添加到Service进程的等待队列或Service进程某一线程的等待队列,Service就可以处理Client的请求了。
接下来看下Native层对Binder的使用。
Binder被实现为一个字符设备,应用程序通过ioctl调用与Binder驱动程序进行通信。首先看实现一个ServiceDemo涉及到的类结构关系。
RefBase是Android实现指针管理的类,牵扯到引用计数的都继承自这个类,然后通过sp,wp实现强引用计数与弱引用计数的管理。
Binder使用Client-Server的通信方式,要实现一个Server,需要先定义一套接口,Client与Server同时实现这套接口,Server端完成实际的功能,Client端只是对Server端功能调用的封装,由于这套接口需要跨进程调用,需要对所有接口一一编号,Server端根据接口编号决定调用什么函数。在上图中对接口的定义就是IServiceDemo。
要实现进程间通信,首先需要定义通信的协议,然后向应用程序提供通信的接口,Binder Driver定义了通信协议,IBinder,BpBinder,BBinder承担了通信接口的工作,IBinder定义了通信的接口,BpBinder是Client访问服务端的代理对象,负责打开Binder设备并与Binder设备通信,BBinder作为服务端与Binder设备通信的接口。Client通过BpBinder连接Binder Driver,然后Binder Driver通过BBinder与Server通信,从而完成进程间通信。
IServiceDemo定义了Client与Server通信的接口,需要Client与Server同时实现,我们已经知道,Client通过BpBinder与Server的BBinder进行通信,那么Client端怎么得到BpBinder,Server端怎么得到BBinder呢?从上图可以看到,IServiceDemo继承自IInterface,其实IInterface就定义了一个方法asBinder,返回一个IBinder对象的指针,应该是通过这个方法获得BpBinder与BBinder对象了。看asBinder实现可以知道,asBinder直接调用了onAsBinder,onAsBinder是一个虚方法,所以是调用子类的具体实现。我们发现,IInterface有两个子类BpInterface与BnInterface,在这两个类中都实现了onAsBinder,在BpInterface中,onAsBinder返回了remote(),remote()其实是返回一个BpBinder对象,后面会看到。在BnInterface中,onAsBinder直接返回this指针,而BnInterface继承自BBinder,所以BnInterface的onAsBinder返回了一个BBinder对象,BpBinder与BBinder都有了,Client就可以与Server通信了。
前面说到remote()返回一个BpBinder对象,那么这个对象是如何返回的呢?从上图看到,BnInterface是继承自BBinder的,但是BpInterface并没有继承自BpBinder,但是我们发现,BpInterface的构造函数接收一个IBinder类型的参数,我们看一下BpInterface的构造函数:
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
: BpRefBase(remote)
{
}
BpInterface继承自BpRefBase,在BpInterface的初始化列表中调用了父类BpRefBase的构造函数,将IBinder remote传了过去。再看BpRefBase的构造函数:
BpRefBase::BpRefBase(const sp<IBinder>& o)
: mRemote(o.get()), mRefs(NULL), mState()
{
extendObjectLifetime(OBJECT_LIFETIME_WEAK); if (mRemote) {
mRemote->incStrong(this); // Removed on first IncStrong().
mRefs = mRemote->createWeak(this); // Held for our entire lifetime.
}
}
直接将BpInterface传过来的IBinder remote保存到了成员mRemote中,而remote()函数就直接返回了这个mRemote对象。
通过BpInterface的构造函数保存了BpBinder对象,那么BpInterface的构造函数是什么时候调用的,而作为构造函数参数传递进去的BpBinder又是什么时候构造的?以ServiceManager为例,实名Binder需要通过addService向ServiceManager注册,这也是进程间通信,那么我们就需要获得ServiceManager的BpBinder,即BpInterface的子类BpServiceManager对象,来看一下BpServiceManager的获取方法:
sp<IServiceManager> defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager; {
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
}
} return gDefaultServiceManager;
}
单例模式,看以上代码的红色部分,ProcessState代表进程对象,每个进程只有一个,在ProcessState::self()中通过单例模式返回每个进程的ProcessState的唯一实例,在ProcessState的函数函数中通过open调用打开了Binder设备,并通过mmap建立了内存映射。open引起binder driver中的binder_open被调用,binder_open中新建binder_proc节点,初始化todo队列与wait队列,并将binder_proc节点保存在binder_open第二个参数struct file *flip的flip->private_data中及binder_procs中。
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
return getStrongProxyForHandle();
} sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result; AutoMutex _l(mLock); handle_entry* e = lookupHandleLocked(handle); if (e != NULL) {
// We need to create a new BpBinder if there isn't currently one, OR we
// are unable to acquire a weak reference on this current one. See comment
// in getWeakProxyForHandle() for more info about this.
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn't have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);
}
} return result;
}
handle是0,lookupHandleLocked的返回结果会是NULL,所以会执行红色部分新建一个BpBinder,defaultServiceManager中红色部分可以简化为:
gDefaultServiceManager = interface_cast<IServiceManager>(new BpBinder());
BpBinder有了,我们在前面也知道了BpBinder会做为参数传递给BpInterface的构造函数,那么BpInterface的构造函数是什么时候调用的?从以上代码看,应该是interface_cast了,将参数BpBinder转化为了BpInterface的子类BpServiceManager,再来看interface_cast的实现。
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
INTERFACE即为IServiceManager,继承自IInterface的类都会声明DELCARE_META_INTERFACE与IMPLEMENT_META_INTERFACE,看一下IMPLEMENT_META_INTERFACE的实现:
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const android::String16 I##INTERFACE::descriptor(NAME); \
const android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const android::sp<android::IBinder>& obj) \
{ \
android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Bp##INTERFACE(obj); \
} \
} \
return intr; \
} \
I##INTERFACE::I##INTERFACE() { } \
I##INTERFACE::~I##INTERFACE() { }
在IMPLEMENT_META_INTERFACE宏中实现了asInterface,上述红色代码中,obj即传进来的BpBinder(0),最上面的图的注释中说了BpBinder的queryLocalInterface返回NULL,所以会执行蓝色代码,INTERFACE是Servicemanager,所以会新建一个BpServiceManager对象。BpServiceManager对象有了,对过其asBinder方法返回的BpBinder对象就可以与Server进行通信了。
Client有了代理对像BpInterface,那么怎么通过这个代理对象与Server进行通信呢?标准方法是下面这样:
remote()->transact(SET_MASTER_VOLUME, data/*parcel*/, &reply/*parcel*/);
前面已经说了,Client通过BpBinder经由Binder驱动、BBinder与Server端通信,从这里看确实是这样,remote()返回BpBinder对象,调用BpBinder的transact来与Server通信,transact是定义在IBinder中的,BpBinder与BBinder都实现了这个方法。
在BpBinder::transact的实现中,直接调用了IPCThreadState::transact,前面说过ProcessState代表进程对象,每个进程有一个,在ProcessState的构造函数会打与Binder设备并进行mmap,而这里的IPCThreadState就表示线程对象,使用LTS(Local Thread Storage)每个线程有一个IPCThreadState对象,Binder通信是线程与线程的通信,这里我们能通过IPCThreadState::transact与Server端进行通信。
IPCThreasState::transact方法首先调用writeTransactionDate将请求数据封装进binder_transaction_data结构并写入Parcel mOut中。然后调用waitForResponse。
waitForResponse会调用talkWithDriver,talkWithDriver通过ioctl(driverFD,BINDER_WIRTE_READ,&binder_write_read)与Binder驱动进行通信,当Server处理完请求后talkWithDriver成功返回,然后waitForResponse中读取Binder Driver返回的指令并执行相应的动作。
在Server中,binder thread的joinThreadPool中会调用taklWithDriver等待Client请求,当有请求到来时talkWithDriver返回,读取command,调用executeCommand处理请求。在executeCommand中调用BBinder的transact处理请求,BBinder::transact会调用虚方法onTransact来完成具体功能,具体实现就是BnServiceManager::onTransact或BnServiceDemo::onTransact等等。一般会有一个类继承自BnXXXXX完成具体功能,在BnXXXXX的onTransact中会调用完成相应功能的接口,由于是虚方法,就会调用到具体实现类。
注册上下文管理者--ServiceManager
通过 ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); 一个进程可以注册成为上下文件管理者,在ServiceManager就是执行这条ioctl请求。
ioctl调用会执行Binder Driver的binder_ioctl函数,binder_ioctl根据第二个参数cmd执行相应的同作,看下BINDER_SET_CONTEXT_MGR对应的处理:
case BINDER_SET_CONTEXT_MGR:
if (binder_context_mgr_node != NULL) {
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto err;
}
if (binder_context_mgr_uid != -) {
if (binder_context_mgr_uid != current->cred->euid) {
printk(KERN_ERR "binder: BINDER_SET_"
"CONTEXT_MGR bad uid %d != %d\n",
current->cred->euid,
binder_context_mgr_uid);
ret = -EPERM;
goto err;
}
} else
binder_context_mgr_uid = current->cred->euid;
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto err;
}
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = ;
binder_context_mgr_node->has_weak_ref = ;
break;
很简单,就是通过binder_new_node获取到一个binder_node保存到全局变量binder_context_mgr_node中,同时保存了UID,只能有一个context_manager。
Binder In Native的更多相关文章
- Binder的Native实现libbinder
libbinder – Binder的Native实现 出于性能和代码统一性的角度考虑,Binder IPC并不Java和Native环境里各实现一次,而只是分别在不同的执行环境里提供使用的接口.使用 ...
- Binder机制,从Java到C (6. Binder in Native : libbinder)
1.Java和C++中的Binder 从前一篇 Binder机制,从Java到C (5. IBinder对象传递形式) 中可以看到,使用Binder的Java代码,到最后都会进入到Native环境,将 ...
- Binder in Java
Android在Native层实现了进程间的Binder通信,但是上层应用程序的开发及Framework的实现都是Java,用Java层再实现一次肯定是不合理的,Java可以通过JNI调用Native ...
- Binder机制,从Java到C (大纲)
转载请标注:张小燕:http://www.cnblogs.com/zhangxinyan/p/3487381.html 前段时间一直在看有关Binder机制的内容,觉得受益匪浅,整理记录于此,大家请随 ...
- IPC 之 Binder 初识
概述 最近在看Android 的 IPC 机制,想要系统的研究一下,然后就走到了 Binder 这里,发现这个东西真是复杂,查看了一下些文章想要记录下.想要自己写但是发现一篇文章已经写的非常好了,就转 ...
- Android Native IPC 方案支持情况
Binder - 不支持Native层的binder 内存共享 - 不支持 信号量(信号灯) - 不支持 消息队列 - 不支持 信号 - 支持,但是不能用sigqueue传消息,只能用来安装信号,可以 ...
- 《深入理解Android 卷III》第二章 深入理解Java Binder和MessageQueue
<深入理解Android 卷III>即将公布.作者是张大伟.此书填补了深入理解Android Framework卷中的一个主要空白,即Android Framework中和UI相关的部分. ...
- 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制【转】
本文转载自:https://blog.csdn.net/freekiteyu/article/details/70082302 Android-Binder进程间通讯机制 概述 最近在学习Binder ...
- Binder进程与线程ProcessState以及IPCThreadState
ProcessState以及IPCThreadState ProcessState是负责打开Binder节点并做mmap映射,IPCThreadState是负责与Binder驱动进行具体的命令交互. ...
随机推荐
- PHP实现RTX发送消息提醒
RTX是腾讯公司推出的企业级即时通信平台,大多数公司都在使用它,但是我们很多时候需要将自己系统或者产品的一些通知实时推送给RTX,这就需要用到RTX的服务端SDK,建议先去看看RTX的SDK开发文档( ...
- 我们是怎么做Code Review的
前几天看了<Code Review 程序员的寄望与哀伤>,想到我们团队开展Code Review也有2年了,结果还算比较满意,有些经验应该可以和大家一起分享.探讨.我们为什么要推行Code ...
- 浅谈WEB页面提速(前端向)
记得面试现在这份工作的时候,一位领导语重心长地谈道——当今的世界是互联网的世界,IT企业之间的竞争是很激烈的,如果一个网页的加载和显示速度,相比别人的站点页面有那么0.1秒的提升,那也是很大的一个成就 ...
- Appium移动自动化框架
引言:Appium 是一个移动端自动化测试开源工具,可以针对不同的平台用一套API来编写测试用例.本文对Appium自动化测试框架的功能进行了概括. 本文选自<软件自动化测试开发>. Ap ...
- JavaScript权威指南 - 数组
JavaScript数组是一种特殊类型的对象. JavaScript数组元素可以为任意类型,最大容纳232-1个元素. JavaScript数组是动态的,有新元素添加时,自动更新length属性. J ...
- [.NET] C# 知识回顾 - Event 事件
C# 知识回顾 - Event 事件 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/6060297.html 序 昨天,通过<C# 知识回顾 - ...
- 在jekyll模板博客中添加网易云模块
最近使用GitHub Pages + Jekyll 搭建了个人博客,作为一名重度音乐患者,博客里面可以不配图,但是不能不配音乐啊. 遂在博客里面引入了网易云模块,这里要感谢网易云的分享机制,对开发者非 ...
- hadoop2.7之Mapper/reducer源码分析
一切从示例程序开始: 示例程序 Hadoop2.7 提供的示例程序WordCount.java package org.apache.hadoop.examples; import java.io.I ...
- Springboot搭建web项目
最近因为项目需要接触了springboot,然后被其快速零配置的特点惊呆了.关于springboot相关的介绍我就不赘述了,大家自行百度google. 一.pom配置 首先,建立一个maven项目,修 ...
- js 入门级常见问题
写在前面:以下是个人总结的关于js常见的入门级的问题一些总结. js是有 ECMAScript Dom Bom 三部分组成. 1,undefined,NaN,Null,infinity 1) unde ...