Andriod Binder原理解析

概述:

Binder通信涉及的源码纷繁复杂,细节更是深得难以细究,本文试图以简洁的语言说明binder原理,以让读者理解binder大致框架和流程,至于具体的细节这里不作陈述,需要读者自己研读源码了.

由于java层的binder只是对c++层binder的封装调用,要想真正理解binder还是要从c++层分析,因此本文所涉及的代码均是c++层.文章最后也会贴个小demo

 

首先,binder是什么?

简单来说binder就是一套通信协议,可以和网络通信类比.

Binder涉及到的几个重要角色:

1.servicemanger----类比dns服务器

2.service---类比具体的网络服务提供方,如百度服务器

3.client---类比个人电脑

4.handle ---类比ip地址

至于BpBinder,BBinder,BpInterface,BnInterface这里先不说

 

我们来看看完整的binder通信流程是什么样的(和网络通信类比)

1.dns服务器先要启动

                   ----servicemanager先启动

2.百度服务器向dns服务器传送自己的ip地址(如1.1.1.1)

                  --- service向servicemanager注册自己的句柄值

3.个人电脑向dns查询百度www.baidu.com的ip地址

                 -- -client通过service名称向servicemager获取service的句柄值

4.个人电脑拿到ip地址后就和百度服务器通信了

                 ---client通过拿到的句柄值和service通信

这里有一点要说明,为什么百度和个人电脑可以和dns直接通信?因为dns有固定的且众所周知的IP如google的8.8.8.8,国内的114.114.114.114

同样service和client可以和servicemanager通信的原因是,servicemanager也有自己固定句柄值0 (servciemanager本身也是一个服务).

 

在开始之前先说明一点,client和service都是运行在用户态,而binder驱动是运行在内核态;运行在用户态的进程不能直接交换数据,所以要通过运行在内核态的binder驱动来交换数据进行通信.

一.首先说说service注册

servicemanager启动不是本文重点,先从service向servicemanger注册开始.话不多说,直接上时序图

 

service通过内核和servicemanager建立通信,并向servicemanger注册自己对应的句柄值.

图示中有几个红色标注的注意事项要说明:

1.注册服务的代码经常是这样的

sp<IServiceManager> sm = defaultServiceManager();

sm->addService(String16("service.testservice"), new BnTestService());

defaultServiceManager()最终获取new BpServiceManager(new BpBinder(0))

所以sm->addService()就是BpServiceManager->addService();

addServic()参数”service.testservice”是服务的名字,可以类比理解成”www.baidu.com”

 

2.内核中ioctl()函数会把请求过来的所有进程封装成binder_proc结构体,并将这些结构体使用双向链表的形式串联起来,以便管理各个进程

 

3.内核binder_transaction()函数会在service对应的binder_proc中新建binder_node,在servicemanager对应的binder_proc建立binder_ref,并让binder_ref指向binder_node让这两个进程保持关联.

最后唤醒servicemanager对应的线程

 

4.servicemanager对应的线程被唤醒后,执行自己的binder_thread_read()

binder_thread_read()向用户空间传送BR_TRANSACTION语义,告诉servicemanager数据已准备好,你可以工作了.

 

5.此时servicemanager可以解析传输过来的数据了.

注意servicemanger中的binder.c和内核binder.c文件不是同一个文件

前者位于frameworks/native/cmds/servicemanager/binder.c

后者位于kernel/androidxxx/drivers/android/binder.c

 

6.servicemanager将service对应的binder句柄值储存在单链表中.

 

 

二. 再看看client如何获取service对应的binder句柄值

时序图如下

client获取service对应的binder句柄值

 

client通过内核和servicemanager建立通信,并从servicemanger中获取service对应的binder句柄值.

红色标注注意点说明:

1.client获取服务的代码经常是这样的

sp<IServiceManager> sm = defaultServiceManager();

sp<IBinder> binder = sm->getService(String16("service.testservice"));

关于sm是什么前文已有说明,sm->getService()就是BpServiceManager->getervice();

这里获取的binder就是BpBinder

2.在service回复client阶段,内核找到service句柄值后,client对应的binder_proc中创建binder_ref,并指向service对应的binder_proc的binder_node,使client和service产生联系,为二者后续通信打下基础.

binder_proc是内核创建的对应用户空间进程的结构体,前文有说,这里再提一下.

3.client拿到service对应的句柄值后,使用该句柄构建新的BpBinder. 后续就可以通过该BpBinder和service通信了

 

三. client和service通信

时序图如下

client和service通信

 

client终于可以和service通信了,下面就标注的红色注意事项做一一说明

1.client一般不直接使用BpBinder,而是通过自定义一个继承BpInterface的子类如BpTestService来使用;

BpInterface父类的成员变量mRemote就是BpBinder,这样就可以通过: 自定义的BpTestService--->BpInterface--->父类mRemote 这样的调用链来使用BpBinder了.

而BpBinder正集成了前面client获取到的service对应的binder句柄值,这样就有了通信的基础.

 

2.binder_transaction()

在内核中通过client对应的binder_proc.binder_ref 找到servcie对应的binder_proc.binder_node. 这样就找到了servcie进程

 

3.service端一般也会自定义一个继承BnInterface并重写onTransact()方法的子类,用来处理client的请求.

4.在service回复client阶段,内核在线程事务栈里找到发起端的线程,从而找到发起端的进程,也就是client进程.

5.从内核写回的数据已经存储到用户空间的Parcel,这个时候client就可以从Parcel中读取读取servcie回复的相关数据了

 

至此,service注册handle, client获取handle, client和servcie通信大体框架已经讲完,最后附上c++层client和service通信demo

代码结构图

1.BnTestService.h

继承BnInterface, 主要实现onTrasact(),处理client端过来的调用

/**
 * BnTestService.h
 *
 * BnInterface:
 *      与BpTestServcie相对应, 继承BBinder
 * 
 * BnTestService:
 *      一般情况下,服务端并不直接使用BnInterface<>,
 *      而是使用新的子类继承它,并重写onTransact()方法
 * 
 */
#include "ITestService.h"
namespace android 
{

    class BnTestService: public BnInterface<ITestService> 
    {
    public:
        //重写BBinder.onTransact()
        virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply ,uint32_t flags = 0);
        /**
         * 继承了ITestService,就必须重写其纯虚拟函数
         * 服务端重写该方法,表示服务端的处理逻辑,将处理结果返回给客户端 
         */
        virtual int test(int x) ;
    };
    int BnTestService::test(int x)
    {
        return x*2;
    }
    //服务端收到数据
    status_t BnTestService::onTransact(uint_t code, const Parcel& data,Parcel* reply, uint32_t flags) 
    {
        switch (code) 
        {
        case TRANSACTION_test: 
            printf("server, BnTestService::onTransact, code: TEST\n");
            CHECK_INTERFACE(ITest, data, reply);
            int arg0 = data.readInt32();
            int ret = test(arg0);
            printf("server called arg0=%d, send=%d\n", arg0, ret);
            reply->writeInt32(ret); //回复client端
            return NO_ERROR;
            break;
        }
        return NO_ERROR;
    }
}// namespace android

2.BpTestService.h

继承BpInterface, 封装BpBinder了,方便客户端调用

/**
 * BpTestService.h
 * 
 * client进程并不直接和BpBinder(Binder代理)通信,而是通过BpInterface的成员函数完成远程调用
 * 
 * BpInterface:
 *      使用了模板技术,而且因为它继承了BpRefBase,所以先天上就聚合了一个mRemote成员,
 *      这个成员记录的就是BpBinder对象
 * 
 * BpTestService:
 *      继承BpInterface<>,是我们实现的代理类。
 *
 */
#include "ITestService.h"
namespace android
{

     class BpTestService: public BpInterface<ITestService>
    {
        public:
            BpTestService(const sp<IBinder>& impl);
            virtual int test(int x);
    };

    BpTestService::BpTestService(const sp<IBinder> &impl)
        : BpInterface<ITestService>(impl) {}

    int BpTestService::test(int x)
    {
   		printf("client, 准备发送数据---> %d\n",x);
   		Parcel data, reply;
   		data.writeInterfaceToken(ITestService::getInterfaceDescriptor());
        data.writeInt32(x);
   		remote()->transact(TRANSACTION_test, data, &reply);
   		printf("client, reply: %d\n", reply.readInt32());
        return reply.readInt32();
	}
}

3.ITestService.cpp

辅助接口.

/**
 *ITestService.cpp
 *
 */
#include "ITestService.h"
#include "BpTestService.h"
namespace android
{
    //对宏DECLARE_META_INTERFACE的实现; 主要实现asInterface()
    IMPLEMENT_META_INTERFACE(TestService, "android.TestServer.ITestService");
} // namespace android

4.ITestService.h

辅助接口。

提供client和service共同的方法标识TRANSACTION_test,这样service收到client的数据就知道client要调用哪个函数

/**
 * ITestService.h
 *
 */
#ifndef __TEST_H__
#define __TEST_H__
#include <stdio.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/IBinder.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
using namespace android;
namespace android
{
    class ITestService : public IInterface
    {
    public:
        //该宏主要定义和ITestService相关的方法
        //如:android::sp<ITestService> asInterface(const android::sp<android::IBinder>& obj);
        //如:ITestService()
        DECLARE_META_INTERFACE(TestService); 

        /**
         * 声明一个函数给客户端和服务端使用
         * 客户端在BpTestService::test()实现,对远程binder封装,以便客户端调用
         * 服务端在BnTestService::test()实现,处理具体实现逻辑
         */
        virtual int test(int x)=0;
    };

    enum
    { //服务端提供方法的标识. 服务端可以提供很多方法,客户端想调用哪个就要传对应标识
        TRANSACTION_test = IBinder::FIRST_CALL_TRANSACTION + 0,
    };

}
#endif

5.client 端入口

/**
 * TestClient.cpp
 */

#include "ITestService.h"

int main()
{
     //返回new BpServiceManager(new BpBinder(0));
    sp<IServiceManager> sm = defaultServiceManager(); 

    //binder是new BpBinder(handle), handle从service_manager获取
    sp<IBinder> binder = sm->getService(String16("service.testservice")); 

    //cs 是new BpTestService(binder)
    sp<ITestService> cs = interface_cast<ITestService>(binder);
    int ret = cs->test(3);
    printf("ret = %d\n", ret);

    return 0;
}

6.service端入口

/**
 * TestServer.cpp
 */

#include "ITestService.h"
#include "BnTestService.h"

int main() {
    sp<ProcessState> proc(ProcessState::self());
    //返回new BpServiceManager(new BpBinder(0))
    sp<IServiceManager> sm = defaultServiceManager();

    //BnTestService间接继承BBinder
    sm->addService(String16("service.testservice"), new BnTestService());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    return 0;
}

 

#Android.mk

LOCAL_PATH := $(call my-dir)

#生成binder service的服务端
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
    libutils \
    libbinder 
LOCAL_MODULE    := TestServer
LOCAL_SRC_FILES := \
    TestServer.cpp \
    ITestService.cpp
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)
 
#生成binder service的测试client端
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := \
    libutils \
    libbinder 
LOCAL_MODULE    := TestClient
LOCAL_SRC_FILES := \
    TestClient.cpp \
    ITestService.cpp
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_PATH := $(LOCAL_PATH)/bin
include $(BUILD_EXECUTABLE)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值