Binder進程間通訊(六)---- Binder進程間通訊庫

基本知識

    android系統應用層使用Binder進程間通訊的時候並非直接和binder驅動程序互動,而是經過了一個封裝庫,咱們稱之爲Binder進程間通訊庫。node

    咱們以前說到了Client組件和Servicr組件,分別對應Binder驅動程序中的Binder實體對象(binder_node)和Binder引用對象(binder_ref)。在Binder通訊庫中也有兩個類和之對應,分別是Binder本地對象BnInterface 表示Service和 Binder代理對象 BpInterface 表示clientandroid

    這兩個接口都定義在/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 ui

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方法。咱們在後面實例中將會看到。spa

    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組件。code

    從上文可知,不管是BBinder類仍是BpBinder類,都須要和binder驅動程序進行交互,可是實際上,他並非本身去操做的,而是經過一個IPCThreadState的類來進行交互。server

    定義在/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結束。

相關文章
相關標籤/搜索