Binder进程间通信(六)---- Binder进程间通信库

老子叫甜甜 提交于 2020-02-29 11:11:48

基本知识

    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结束。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!