基本知识
android系统应用层使用Binder进程间通信的时候并不是直接和binder驱动程序互动,而是通过了一个封装库,我们称之为Binder进程间通信库。
我们之前说到了Client组件和Servicr组件,分别对应Binder驱动程序中的Binder实体对象(binder_node)和Binder引用对象(binder_ref)。在Binder通信库中也有两个类和之对应,分别是Binder本地对象BnInterface 表示Service和 Binder代理对象 BpInterface 表示client
这两个接口都定义在/frameworks/native/include/binder/IInterface.h 文件中。
// ----------------------------------------------------------------------
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();
};
// ----------------------------------------------------------------------
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
explicit BpInterface(const sp<IBinder>& remote);
protected:
virtual IBinder* onAsBinder();
};
// ----------------------------------------------------------------------
这里用到了一种很有意思的写法,用模板参数Interface表示一个类,然后BnInterface和BpInterface都直接继承了这个类。至于INTERFACE具体是什么,后面再看。
BnInterface继承了BBinder类,定义在/frameworks/native/include/binder/Binder.h
class BBinder : public IBinder
{
....
virtual status_t transact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
....
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
....
};
关键是其中的两个函数,当Binder代理对象(Client) 通过Binder驱动程序向一个Binder本地对象(Service)发出一个进程间通信请求时,Binder驱动程序会调用 Binder本地对象(Service)的 transact方法。我们在后面实例中将会看到。
BpInterface继承了BpRefBase,同样定义在/frameworks/native/include/binder/Binder.h 中。其中有一个非常重要的成员变量mRemote,他会指向一个BpBinder对象。
BpBinder定义在/frameworks/native/include/binder/BpBinder.h 该类中有一个成员变量mHandle是一个整数,代表一个句柄,直接指向了Binder驱动程序中的Client组件,也就是一个binder_ref的结构体。这里就建立了binder驱动程序和binder进程间通信库的对应关系。
BpBinder中的函数transact方法用来向Server进程中的Service组件发送进程间通信请求(当然这是通过Binder驱动程序来实现的。)这个方法会把mHander和通信数据发送给驱动程序,这样,驱动程序就能够先找到相应的Client组件(binder_ref),继而找到对应的Service组件(binder_node),最后将数据发送给这个Service组件。
从上文可知,无论是BBinder类还是BpBinder类,都需要和binder驱动程序进行交互,但是实际上,他并不是自己去操作的,而是通过一个IPCThreadState的类来进行交互。
定义在/frameworks/native/include/binder/IPCThreadState.h 我们知道,每一个使用binder进程间通信的进程都会维护一个线程池,每一个线程都会对应一个IPCThreadState对象。
其中成员变量self指向自己,成员函数transanct用来和驱动程序交互(内部进一步会调用成员函数talkWithDriver)。
IPCThreadState 内部有一个成员变量mProcess,是一个ProcessState对象,对应使用了进程间通信的进程。它会负责初始化Binder设备(打开/dev/binder设备文件),将设备文件映射到进程的地址空间。
定义在/frameworks/native/include/binder/ProcessState.h 类中有一个静态方法self,通过调用ProcessState.self就能过获取进程唯一ProcessState对象(该对象是在第一次调用ProcessState.self方法时创建,同时调用函数open打开设备文件,调用mmap映射地址空间,并且将用户空间的虚拟地址空间的地址存放在mVMStart中)。
案例
假设我们存在一个硬件设备叫做Frag,该硬件设备是一个简单的寄存器,只有简单的存取功能。现在我们有一个service进程,用来管理这个设备,同时向外提供了访问服务。
现在有一个client进程想要访问这个设备,这就涉及到进程通信了,我们尝试实现这个架构。
显而易见,总归有两个模块,一个server模块,用来表示service进程。一个client模块,用来表示 client进程。但是,对于两个想要通信的进程来说,虽然有了沟通途径(binder),但是还需要定义一门沟通语言。在binder进程间通信中,我们把这个共同语言叫做 服务接口 。只有定义了这个之后,才能无障碍交流。所以还存在第三个模块common(server 和 client都要使用到)
其中服务接口都会继承与 IInterface 接口,顺带一提这个接口定义如下
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;
};
几乎只有一个asBinder方法而已。
对于本例,我们定义了一个IFregService
#define FREG_SERVICE "shy.luo.FregService"
using namespace android;
class IFregService: public IInterface
{
public:
DECLARE_META_INTERFACE(FregService);
virtual int32_t getVal() = 0;
virtual void setVal(int32_t val) = 0;
};
该方法定义了getVal和setVal。另外 DECLARE_META_INTERFACE是一个宏,定义在/frameworks/native/include/binder/IInterface.h 。里面预先写好了IInterface实现类需要重写的方法,主要是对这个组件进行命名(descriptor)和一个asInterface方法,该方法的作用主要是通过一个IBinder对象来获取对应的代理对象,通过代理对象向service进程请求功能。
相对的还有一个用来实现的宏IMPLEMENT_META_INTERFACE同样定义在/frameworks/native/include/binder/IInterface.h ,主要是用来实现一个asInterface方法。
除了服务接口之外,我们还需要实现两个类,分别继承与BnInterface和BpInterface,表示真正的本地对象和代理对象。
class BnFregService: public BnInterface<IFregService>
{
public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
class BpFregService: public BpInterface<IFregService>
{
public:
BpFregService(const sp<IBinder>& impl)
: BpInterface<IFregService>(impl)
{
}
public:
int32_t getVal()
{
Parcel data;
data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
Parcel reply;
remote()->transact(GET_VAL, data, &reply);
int32_t val = reply.readInt32();
return val;
}
void setVal(int32_t val)
{
Parcel data;
data.writeInterfaceToken(IFregService::getInterfaceDescriptor());
data.writeInt32(val);
Parcel reply;
remote()->transact(SET_VAL, data, &reply);
}
};
BpFregService的实现相对比较容易理解,getVal和setVal实际操作上并么有区别,都是首先将参数防撞在一个Parcel对象中,然后调用父类BpBinder的transact向service组件发送请求(当然这个会通过binder驱动程序)。
BnFregService虽然是一个本地对象,但是他并不是负责实现setVal和getVal的实现(实现会交给它的子类,这里是FregService),主要是实现了上面介绍的onTransact方法,用来分发收到的命令。
status_t BnFregService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code)
{
case GET_VAL:
{
CHECK_INTERFACE(IFregService, data, reply);
int32_t val = getVal();
reply->writeInt32(val);
return NO_ERROR;
}
case SET_VAL:
{
CHECK_INTERFACE(IFregService, data, reply);
int32_t val = data.readInt32();
setVal(val);
return NO_ERROR;
}
default:
{
return BBinder::onTransact(code, data, reply, flags);
}
}
}
比如这里我们定义了两个命令GET_VAL和SET_VAL(上面的BpFregService的setVal和getVal都会发送相应的命令。)分别交给子类的getVal和setVal方法去处理。
common模块的内容大概就是这么多,接下去是service模块,真正实现getVal和setVal功能,并向外提供服务的模块
class FregService : public BnFregService
{
public:
FregService()
{
fd = open(FREG_DEVICE_NAME, O_RDWR);
if(fd == -1) {
LOGE("Failed to open device %s.\n", FREG_DEVICE_NAME);
}
}
virtual ~FregService()
{
if(fd != -1) {
close(fd);
}
}
public:
static void instantiate()
{
defaultServiceManager()->addService(String16(FREG_SERVICE), new FregService());
}
int32_t getVal()
{
int32_t val = 0;
if(fd != -1) {
read(fd, &val, sizeof(val));
}
return val;
}
void setVal(int32_t val)
{
if(fd != -1) {
write(fd, &val, sizeof(val));
}
}
private:
int fd;
};
int main(int argc, char** argv)
{
FregService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return 0;
}
就FregService类来说并不复杂,继承了BnFregService,主要的作用就是通过open打开设备文件,然后通过write和read来操作设备文件(这不是binder的工作,而是设备驱动本身的工作内容)。
主要的工作内容还是集中在main方法上,首先调用
defaultServiceManager()->addService(String16(FREG_SERVICE), new FregService());
该代码的主要作用就是将本service注册到ServiceManager中(实现我们以后会看到)。然后调用startThreadPool来启动一个Binder线程池,然后通过joinThreadPool将当前线程添加到线程池中,这样就能够开始处理客户端的请求了。
客户端的代码如下
int main()
{
sp<IBinder> binder = defaultServiceManager()->getService(String16(FREG_SERVICE));
if(binder == NULL) {
LOGE("Failed to get freg service: %s.\n", FREG_SERVICE);
return -1;
}
sp<IFregService> service = IFregService::asInterface(binder);
if(service == NULL) {
LOGE("Failed to get freg service interface.\n");
return -2;
}
printf("Read original value from FregService:\n");
int32_t val = service->getVal();
printf(" %d.\n", val);
printf("Add value 1 to FregService.\n");
val += 1;
service->setVal(val);
printf("Read the value from FregService again:\n");
val = service->getVal();
printf(" %d.\n", val);
return 0;
}
首先通过getService从ServiceManager中讯中相应的服务类,返回值是一个IBinder对象,然后将IBinder转换成为定义的IFregService子类,实际上就是BpFregService类型的对象。
至此整个demo结束。
来源:oschina
链接:https://my.oschina.net/u/2398062/blog/1607280