文章目录
1. Linux进程基础
Android系统是基于Linux内核的操作系统,在学习Binder通信之前需要了解一些Linux系统的基本概念
1.1 进程隔离
进程隔离是操作系统内核对于资源管理和安全增强的特性,其最终的目的是让操作系统能够更好的控制程序对资源的申请和使用,控制此程序可访问资源的范围并限定此程序异常之后能够影响的范围。在Linux系统中,不同进程之间数据是不共享的;对于每个进程来说,他无法访问其他进程中的数据,因此一个进程需要与另外一个进程通信,需要某种系统机制才能完成。
1.2 用户空间/内核空间
详细解释可以参考Kernel Space Definition: http://www.linfo.org/kernel_space.html
在Linux系统中,对于Kernel这么一个高安全级别的东西,系统显然是不容许其它的应用程序随便访问的,所以需要对Kernel提供一定的保护机制,这个保护机制用来告诉那些应用程序,你只可以访问某些许可的资源,不许可的资源是拒绝被访问的,于是就把Kernel和上层的应用程序抽像的隔离开,分别称之为用户空间和内核空间
虽然从逻辑上抽离出用户空间和内核空间;但是不可避免的的是,总有那么一些用户空间需要访问内核空间的资源;比如应用程序访问文件,网络是很常见的事情,怎么办呢?用户空间访问内核空间的唯一方式就是系统调用;通过这个统一的接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。
1.3 内核模块/驱动
通过系统调用,用户空间可以访问内核空间,那么如果一个用户空间想与另外一个用户空间进行通信怎么办呢?很自然想到的是让操作系统内核添加支持;传统的Linux通信机制,比如Socket,管道等都是内核支持的;但是Binder并不是Linux内核的一部分,Android系统通过添加一个内核模块运行在内核空间,用户进程之间的通过这个模块作为桥梁,就可以完成通信了。
在Android系统中,这个运行在内核空间的,负责各个用户进程通过Binder通信的内核模块叫做Binder驱动
1.4 图解
图解:
- 每一个进程包含用户空间和内核空间
- 进程A和进程B的用户空间是不共享的,但是他们的内核空间通过Binder驱动关联,这样进程A和进程B就可以通过Binder驱动进行通信
- 用户空间访问内核空间的唯一方式就是系统调用,这样保证了系统的安全性
2. 为什么要使用Binder
前面说过,在传统的Linux上,还是有很多选择可以用来实现进程间通信,比如Socket,管道,共享内存等方式。那么Android为什么不使用这些原有的技术,而是要使开发一种新的叫Binder的进程间通信机制呢?
2.1 安全方面
传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如Socket通信ip地址是客户端手动填入,很容易进行伪造,而Binder机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。
2.2 性能方面(一次数据拷贝)
传统的IPC ,例如Pipe和Socket,执行一次通信需要两次数据拷贝
而Binder仅需要一次内存拷贝就实现了进程间通信从而提高了运行效率,那么他是如何实现的呢?
- 在进程创建时会调用到binder_open函数,在该函数中会打开binder设备文件(/dev/binder)然后调用mmap函数
- mmap函数属于系统调用,mmap会在内核空间中分配一块内存,之后将这块内存映射到用户空间,这样就可以像操作用户空间那样操作内核空间,这是一次拷贝的基础。
- 当数据从用户空间拷贝到内核空间的时候,是直接从当前进程的用户空间拷贝到目标进程的内核空间,这个过程是在请求端线程中处理的,操作对象是目标进程的内核空间。
- 而由于Binder内核空间的数据能直接映射到用户空间,这里就不在需要拷贝到用户空间。这就是一次拷贝的原理
3. Binder的Client/Server通信模型
在Android系统的Binder机制中,由4个组件Client、Server、Service Manager和Binder Driver组成。Binder就是一种把这四个组件粘合在一起的粘结剂了,其中,核心组件是Binder Driver,Service Manager提供了辅助管理的功能,Client和Server正是在Binder Driver和Service Manager提供的基础设施上,进行Client-Server之间的通信。Service Manager和Binder Driver已经在Android平台中实现好,开发者只要按照规范实现自己的Client和Server组件就可以了。
- Client、Server和Service Manager实现在用户空间中,Binder Driver实现在内核空间中
- 在启动过程中Server进程会先注册一些Service到ServiceManager中,所以Server进程是ServiceManager的客户端,而ServiceManager就是服务端了。ServiceManager是一个守护进程,他是service的管理者,能够管理注册的Service并向Client提供查询Service的接口。
- 如果某个Client进程要使用某个Service,必须到ServiceManager中获取该Service的相关信息,所以Client是ServiceManager的客户端,另外Client根据得到的Service信息与Service所在的Server进程建立通信的通路,然后就可以直接与Service交互了,所以,Client是Server的客户端。
- Binder Driver提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信
- Binder Driver和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Server
- 三者的交互都是基于Binder通信的,所以通过任意两者之间的关系都能够揭示Binder的奥秘。
4. Binder的上层实现
先给一张Binder相关的类图一瞰Binder全貌。
通常来说,接口是分析代码的入口,Android中’I’ 打头的类统统是接口类,从上图中我们可以看到BBinder和BpBinder都继承自IBinder,所以我们先从IBinder开始下手。
// frameworks/native/libs/binder/include/binder/IBinder.h
class IBinder : public virtual RefBase
{
public:
...
virtual sp<IInterface> queryLocalInterface(const String16& descriptor); //返回一个IInterface对象
...
virtual const String16& getInterfaceDescriptor() const = 0;
virtual bool isBinderAlive() const = 0;
virtual status_t pingBinder() = 0;
virtual status_t dump(int fd, const Vector<String16>& args) = 0;
virtual status_t transact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0) = 0;
virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0) = 0;
virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = NULL) = 0;
...
virtual BBinder* localBinder(); //返回一个BBinder对象
virtual BpBinder* remoteBinder(); //返回一个BpBinder对象
};
有接口必然有实现,从图中可以看出,BBinder和BpBinder都是IBinder的实现类,它们干啥用的,有啥区别?有兴趣同学可以去分别去读读他们的代码,分别在
Bpinder: frameworks/native/libs/binder/BpBinder.cpp
BBinder: frameworks/native/libs/binder/Binder.cpp
这里我们简单总结一下他们的区别:
接口 | BBinder | BpBinder |
---|---|---|
queryLocalInterface() | 没有实现 ,IBinder 默认实现 return NULL; | 没有实现 ,IBinder 默认实现 return NULL; |
getInterfaceDescriptor() | return sEmptyDescriptor; | (this)->transact(INTERFACE_TRANSACTION, send, &reply); … mDescriptorCache = res; |
isBinderAlive() | return true; | return mAlive != 0; |
pingBinder() | return NoError; | transact(PING_TRANSACTION, send, &reply); |
linkToDeath() | return INVALID_OPERATION; | self->requestDeathNotification(mHandle, this); |
unlinkToDeath() | return INVALID_OPERATION; | self->clearDeathNotification(mHandle, this); |
localBinder() | return this; | 没有实现, IBinder默认实现 return NULL; |
remoteBinder() | 没有实现,IBinder默认实现 return NULL; | return this |
transact() | err = onTransact(code, data, reply, flags); | IPCThreadState::self()->transact(mHandle, code, data, reply, flags); |
onTransact() | switch (code) { case INTERFACE_TRANSACTION: … default: … } | 没有实现 |
它们的差异在于它们是通信两端的不同实现,BBinder是服务端,而BpBinder是客户端。为什么这么说:
- pingBinder, BBinder直接返回OK,而BpBinder需要运行一个transact函数,直接返回的应该是服务端
- linkToDeath()是用来在服务挂的时候通知客户端的,那服务端当然不需要自己监视自己咯,所以BBinder直接返回非法,而Bpbinder需要通过requestDeathNotification()要求某人完成这个事情
- 在Android中,remote一般代表某个远端对象的本地代理,所以remoteBinder()在BBinder中没有实现而在BpBinder中返回自身
所以结论是,BBinder代表着服务端,而BpBinder则是它在客户端的代理
客户端通过BpBinder的transact()发起请求,而服务器端的BBinder在onTranscat()里响应请求,并将结果返回。
4.1 Binder Server端的实现
我们先看一看Binder Server端的实现(以MediaPlayer为例):
这里先给一张Binder Server端的类图,后面在分析代码的时候可以结合这张图去理解,这样理解起来还比较轻松。
前面已经给出了IBinder的代码,我们这里从BBinder开始分析
// frameworks/native/libs/binder/include/binder/Binder.h
class BBinder : public IBinder
{
public:
BBinder();
virtual const String16& getInterfaceDescriptor() const;
virtual bool isBinderAlive() const;
virtual status_t pingBinder();
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t transact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0);
virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
void* cookie = NULL,
uint32_t flags = 0,
wp<DeathRecipient>* outRecipient = NULL);
virtual void attachObject( const void* objectID,
void* object,
void* cleanupCookie,
object_cleanup_func func);
virtual void* findObject(const void* objectID) const;
virtual void detachObject(const void* objectID);
virtual BBinder* localBinder();
protected:
virtual ~BBinder();
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
private:
BBinder(const BBinder& o);
BBinder& operator=(const BBinder& o);
class Extras;
std::atomic<Extras*> mExtras;
void* mReserved0;
};
BBinder类继承了IBinder并从Server端的角度实现了其中大部分方法,但是并没有实现queryLocalInterface()方法,那么一定有一个BBinder的子类实现了这个方法接着看下面的代码
// frameworks/native/libs/binder/include/binder/IInterface.h
// 类BnInterface定义
template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
virtual const String16& getInterfaceDescriptor() const;
protected:
virtual IBinder* onAsBinder();
};
// 函数queryLocalInterface()的实现
template<typename INTERFACE>
inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
const String16& _descriptor)
{
if (_descriptor == INTERFACE::descriptor) return this;
return NULL;
}
可以看到queryLocalInterface()函数是在BBinder的子类BnInterface中实现的,该方法将自己强制转换成 IInterface对象返回,这个IInterface又是什么?显然IInterface不存在于BBinder中,通过上面的代码我们可以看到类BnInterface不仅继承了BBinder还继承了一个模板类INTERFACE,接下来我们看看这个模板类INTERFACE是什么
// frameworks/av/media/libmedia/include/media/IMediaPlayer.h
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
在我们这个例子中BnMediaPlayer继承自BnInterface那么这个INTERFACE模板类就是IMediaPlayer,我们继续跟进
// frameworks/av/media/libmedia/include/media/IMediaPlayer.h
class IMediaPlayer: public IInterface
{
public:
...
virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
virtual status_t setDataSource(const sp<IStreamSource>& source) = 0;
virtual status_t setDataSource(const sp<IDataSource>& source) = 0;
virtual status_t setVideoSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) = 0;
virtual status_t getBufferingSettings(
BufferingSettings* buffering /* nonnull */) = 0;
virtual status_t setBufferingSettings(const BufferingSettings& buffering) = 0;
virtual status_t prepareAsync() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
...
}
找到了!IMediaPlayer继承了IInterface,IMediaPlayer中定义了很多接口,这些接口都是MediaServer向Client端所提供的。
// frameworks/native/libs/binder/include/binder/IInterface.h
class IInterface : public virtual RefBase
{
public:
IInterface();
static sp<IBinder> asBinder(const IInterface*);
static sp<IBinder> asBinder(const sp<IInterface>&);
protected:
virtual ~IInterface();
virtual IBinder* onAsBinder() = 0;
};
IInterface中定义了从Interface 到IBinder的转换接口asBinder(), 和刚才我们研究的queryLocalInterface() 正好反过来,说明IBinder 和 IInterface 之间是可以互转的。一个对象怎么可以变成另外一个对象呢?唯一的解释就是这个对象具有双重身份,要么他同时继承 IInterface 和 IBinder, 要么他体内有这两个对象同时存在。我们回头看BnMediaPlayer类
// frameworks/av/media/libmedia/include/media/IMediaPlayer.h
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
class IMediaPlayer: public IInterface
{
public:
...
}
可以看出BnMediaPlayer既是IBinder的子类,也是IInterface的子类。这便是IBinder和IInterface可以相互转换的原因。
到这里Binder Server端的实现已经介绍完了,读者可以结合前面的类图再回顾一遍感受一下。
4.2 Binder Client端的实现
Server端的实现介绍完了接下来介绍Client端的实现,在Client端我们需要找到一个类,它必须同时拥有IBinder 和 IIterface的特性。
IIterface的子类IMediaPlayer中定义了Server端为Client端提供的方法,我们可以理解为通信协议。而IBinder提供了两者之间通信的通路,要想实现通信两者都是必须的。
接下来先看BpBinder
// frameworks/native/libs/binder/include/binder/BpBinder.h
class BpBinder : public IBinder
跟IInterface 没有关系,那么一定是别人,看看BpInterface
// frameworks/native/libs/binder/include/binder/IInterface.h
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
BpInterface(const sp<IBinder>& remote);
protected:
virtual IBinder* onAsBinder();
};
这里的INTERFACE 是 IMediaPlayer, 它继承了IInterface, IInterface 的对象找到了, BpBinder和IInterface我们都找到了但是他们之间的关系是什么呢?接下来我们看BpRefBase ,
// frameworks/native/libs/binder/include/binder/Binder.h
class BpRefBase : public virtual RefBase
{
protected:
...
inline IBinder* remote() { return mRemote; }
...
private:
...
IBinder* const mRemote;
RefBase::weakref_type* mRefs;
volatile int32_t mState;
};
有了,BpRefBase 里有IBinder 成员变量mRemote,看来在客户端,没有一个类同时继承IBinder 和 IInterface, 但是有一个类继承了其一,但包含了另外一个,这种在设计模式里称为组合(Composition).
还是不太明白?还是用图解释吧。
看明白了?从BpInterface开始,通过BpRefBase 我们可以找到IBinder对象mRemote, 这个转换就在 asBinder() 的实现里,看看代码
// frameworks/native/libs/binder/include/binder/IInterface.h
sp<IBinder> IInterface::asBinder(){
return this ? onAsBinder() : NULL;
}
sp<const IBinder> IInterface::asBinder() const{
return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
}
template<typename INTERFACE>
inline IBinder* BpInterface<INTERFACE>::onAsBinder()
{
return remote();
}
template<typename INTERFACE>
IBinder* BnInterface<INTERFACE>::onAsBinder()
{
return this;
}
这里印证我们上面两张图的正确性,onAsBinder是转换的发生的地方,服务端(BnInterface)的实现直接返回了自己,因为它继承了两者,而客户端BpInterface则需要通过remote()函数获取mRemote 成员变量,因为他自己本身不是IBinder。
接下来我们看看BpRefbase类中的mRemote是如何被赋值的?看看以下代码
//frameworks/native/libs/binder/Binder.cpp
BpRefBase::BpRefBase(const sp<IBinder>& o)
: mRemote(o.get()), mRefs(NULL), mState(0)
{
...
}
// frameworks/native/libs/binder/include/binder/IInterface.h
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
: BpRefBase(remote)
{
}
// frameworks/av/media/libmedia/IMediaPlayer.cpp
class BpMediaPlayer: public BpInterface<IMediaPlayer>
{
public:
BpMediaPlayer(const sp<IBinder>& impl)
: BpInterface<IMediaPlayer>(impl)
{
}
...
}
结合类图和上面的代码我们可以得出结论:BpRefBase内的mRemote对象是从子类BpMediaPlayer一级一级传上去的。那唯一的问题就是在哪里完成的这个注入操作,搜索"new BpMediaPlayer",代码里没有,试试搜索"IMediaPlayer“,发现了一点线索
// frameworks/av/media/libmedia/IMediaPlayerService.cpp
virtual sp<IMediaPlayer> create(
const sp<IMediaPlayerClient>& client, int audioSessionId) {
Parcel data, reply;
...
// 和Binder驱动进行交互,这部分内容在Android跨进程通信Binder原理分析(二)中分析
remote()->transact(CREATE, data, &reply);
return interface_cast<IMediaPlayer>(reply.readStrongBinder()); //从reply里读出IBinder,然后转成IMediaPlayer接口对象
}
reply.readStrongBinder()会根据Binder驱动返回的内容创建IBinder对象(具体创建过程在在Android跨进程通信Binder原理分析(二)中分析),然后通过interface_cast 把IBinder 转换成了 IMediaPlayer, interface_cast 又是什么,他是怎么转换的?
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
继续跟进 asInterface()函数, 结果发现里以下代码
// frameworks/native/libs/binder/include/binder/IInterface.h
#define DECLARE_META_INTERFACE(INTERFACE) \
static const android::String16 descriptor; \
static android::sp<I##INTERFACE> asInterface( \
const android::sp<android::IBinder>& obj); \
virtual const android::String16& getInterfaceDescriptor() const; \
I##INTERFACE(); \
virtual ~I##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; \
}
恍然大悟,原来在DECLARE_META_INTERFACE 这个宏里定义了asInterface, 在IMPLEMENT_META_INTERFACE 里实现了它,这里果然有一个new BpMediaPlayer! BpRefBase内的mRemote对象是从子类BpMediaPlayer一级一级传上去的,就是从这里开始的。
这里总结一下:
- Client端通过Binder驱动获取一个IBinder对象并通过interface_cast转成IMediaPlayer接口对象,IMediaPlayer就是用户程序看到的对象,客户端通过其调用IMediaPlayer的接口方法,最终调到BpBinder的transact(),进而调用到BBinder的onTransact();
- 在interface_cast中生成BpMediaPlayer,由于BpMediaPlayer是BpRefBase的子类,所以在构造的时候将IBinder对象一级一级的传到BpRefBase并保存在成员变量mRemote中
- 这样BpInterface不仅是IInterface的子类,还拥有IBinder的成员变量,所以BpInterface同时拥有IBinder 和 IIterface的特性
为什么要把Binder搞得那么复杂?Google这么做是希望通过这些封装尽可能减少开发者的工作量,这样在开发一个Native的Service 的时候,开发者只需要做这么几件事:
- 定义一个接口文件, IXXXService, 继承IInterface
- 定义BnXXXService(),继承 BnInterface<IXXXService>
- 定义BpXXXService(),继承BpInterface<IXXXService>
- 在BnXXXService中实现XXXService的服务端。
- 在BpXXXService中实现XXXService的代理端。
(其实从Android7.0开始,也可以通过aidl的方式实现Native的Service,这种方式简化了Server的开发流程,有兴趣的可以了解一下)
而在Client端使用Service的时候,让访问远端服务就像调用本地函数一样,Client端只需要通过ServiceManager获取远端服务的代理(创建对象),然后通过这个代理调用远端服务的函数即可(调用对象中的方法)。