Binder 跨进程通信原理浅析

关于跨进程:
为什么要跨进程呢?在Android系统中,每个进程都有分配自己的内存空间,各进程间是不能直接访问其他进程的内存的,那当一个程序要操作与另一个程序的方法怎么办呢?(比如在应用程序中隐藏SystemUI的导航栏)这时就需要跨进程通信了。Binder就是一个帮助进程进通信的虚拟设备,为什么是虚拟设备呢,因为它没有硬件,只只用代码实现的通信架构。

从哪开始说?
Android系统首次开机启动时,会启动很多系统服务,然后将这些服务添加到ServiceManager进行统一管理。添加这些服务时就需要跨进程通信,我们就从这里开始吧。对于Binder的各个元素就像TCP/IP网络
o. Binder 驱动 –> 路由器
o. ServiceManager –> DNS
o. Binder Client(BpBinder) –> 客户端
o. Binder Service(BnBinder) –> 服务端
*


下图为Binder通信的简要原理:
这里写图片描述

关于ServiceManager:
系统服务众多,为了方便,就需要一个总管,ServiceManger就是这个总管,集中管理系统内的所有服务,它能施加权限控制,并不是任何进程都能注册服务。就像DNS(它的IP地址为0),可以通过字符串去查找所对应的服务。 ServiceManger自身也同样是Binder Service。Android系统开机启动时 ServiceManger会抢先就位。和其他Native服务启动一样,首先启动一个自己的进程,打开Binder驱动,分配内存大小等。不同的是会将自己设置为Binder的大管家,整个Android系统允许有一个ServiceManger存在。后面还有设置为管家的指令会是失败。然后ServiceManger会进入循环,等待客户请求。

跨进程通信的例子
系统服务启动的时候会将自己注册到ServiceManger,比如StatusBarManagerService。

Slog.i(TAG, "Status Bar");
statusBar = new StatusBarManagerService(context);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);

frameworks\base\core\java\android\osServiceManager.java –>addService():

    public static void addService(String name, IBinder service) {
        try {
            getIServiceManager().addService(name, service, false);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

我们先弄清楚getIServiceManager()是啥。看名字就能理解这是获取ServiceManager服务。ServiceManager进程肯定不和当前程序一个进程,此时的跨进程通信已经开始了。(这里不用去DNS找IP了,因为我们已经知道了这个“IP”,就是访问它自己,现在是直接拿IP去Binder”路由器”,看路由器是怎么访问另一端的服务的)

一,通信开始

①首先我们要拿到客户端的java层的代理BpServiceXXX(在JAVA层客户端这边需要它想驱动发送请求)

frameworks\base\core\java\android\osServiceManager.java –>getIServiceManager() :

private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }

这个BinderInternal.getContextObject()又是什么呢?其实他返回的是Native层的代理,原来Java层代理只是对Native层的一个封装

BinderInternal.getContextObject()是其父类IBinder的一个内部类,用来获取ServiceManager的一个Native代理:
frameworks\base\core\jni\android_util_Binder.cpp

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

看到这里是不是很多小伙伴更加懵逼了?不要急 ,我们一点点吧谜底揭开。(ProcessState::self(),这里所做的就是上图的映射一块内存地址。它的构造方法做作的大概就是创建一个进程,打开Binder驱动,分配内存地址什么的,具体这里就不讲了。)我们看看ProcessState.cpp里的getContextObject(NULL)。
frameworks\native\libs\binder\ProcessState.cpp

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    //这里传入了0,也就是ServiceManager这个"DNS"的IP地址
    return getStrongProxyForHandle(0);//返回的BpServiceXXX(BpBinder())
}
... ...
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
        //第一次获取的话肯定走这里
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
        //这个handle就是前面传进来的那个0
            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            //可以看到这里创建了一个handle值为零的BpBinder对象
            result = b;
        } else {.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

返回一个BpBinder,它就是Navtive的代理,而javaObjectForIBinder(env, BpBinder(0)),为了保证Java层和Navtive的数据类型适配,进而的再一次封装,而java代理对此在进行一次封装,来协议化需要传递的数据

是不是快忘了我们从哪里出发了?
①sServiceManager .addService();
②sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
相当于ServiceManagerNative.asInterface(BpBinder(0));
frameworks\base\core\java\android\os\ServiceManagerNative.java–>asInterface():

static public IServiceManager asInterface(IBinder obj)
    {  
    ... ...
    //返回ServiceManagerProxy(BpBinder(0)),就是ServiceManager的客户端代理
        return new ServiceManagerProxy(obj);
    }

所以ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);就等效于
ServiceManagerProxy(javaObjectForIBinder(env, BpBinder(0)).addService(Context.STATUS_BAR_SERVICE, statusBar);

ServiceManagerProxy就是BpServiceXXX ,是客户端的一个代理,通过代理向服务端发送请求 。而BpBinder是真正想Binder驱动发送请求的实现类。(BpBinder和BBinder的爸爸都是IBinder)

二,现在我们拿到了ServiceManager JAVA层的代理对象,可以开始请求通信了

先看看ServiceManager JAVA层的代理的实现

ServiceManagerProxy类的构造方法。
ServiceManagerProxy是ServiceManagerNative的一个内部类
ServiceManagerProxy.java –>

//好像并没有做什么
public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;//把BpBinder交给了自己的成员
    }

开始通信发送请求:ServiceManagerProxy.addService():

public void addService(String name, IBinder service, boolean allowIsolated)
           throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        data.writeStrongBinder(service);
        data.writeInt(allowIsolated ? 1 : 0);
        //这里才开始通信,向服务端发送数据和请求
        mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
        reply.recycle();
        data.recycle();
    }

讲了这么多了,我们来理一理:
首先通过IBinder的内部类方法BinderInternal.getContextObject()得到ServiceManager的Native层代理。然后痛过asInterface()封装,返回一个erviceManagerProxy也就是架构中的JAVA层代理对象,然后通过它发送请求进行通信

三,通信原理
这个神秘transact()方法到底是怎么怎么想Binder驱动发送请求的呢?我们继续看。mRemote是BpBinder类型,所以这个方法就在BpBinder中。
frameworks\native\libs\binder\BpBinder.cpp –>transact():

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
        //mHandle的值为0
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

IPCThreadState,每一个进程都会有一个这样的线程,Binder通信真正的执行者就是。IPCThreadState::self()所做的和ProcessState::self(),开启一个这样的线程,分配缓存区,然后把自己保存起来。有两个容器一个接收来自Binder驱动返回过来的数据(mIn),一个用于向Binder发送数据(mOUt)。

frameworks\native\libs\binder\IPCThreadState.cpp–>transact():

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();
    ... ...
   if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
            //发送请求
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }

    ... ...
    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
        //处理请求
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif

        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {

        err = waitForResponse(NULL, NULL);
    }

    ... ...
}

这段代码主要干了两件事:①发送请求err = writeTransactionData();②处理返回结果err = waitForResponse()。我们再来看看这两个函数;
err = writeTransactionData()只是将请求数据打包写到用于给Binder发送数据的容器mOut中,waitForResponse()方法中有talkWithDriver()时和驱动打交道(通知Binder驱动有数据请求)的实现,并把返回数据保存到接收放在mIn中。然后一一对应处理。到此为止客户端所做的就这么多了。
这里写图片描述

四,服务端收到请求后的反应

ServiceManager会在一开始系统启动中就启动,并把自己注册在Binder驱动中,把自己升为“大管家”,进入一个死循环,不断的读取Binder发来的请求,如果有请求就进行处理

服务端的BBinder和BpBinder都继承与IBinder,都实现transact()。BpBinder是客户端的代理,而BBinder则是服务端的实行者。

Binder驱动收到请求,找到对应的服务端,并执行BBinder的transact()方法对请求进行处理:
frameworks\native\libs\binder\Binder.cpp:

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);

    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
        //执行了子类的onTransact()方法
            err = onTransact(code, data, reply, flags);
            break;
    }

    if (reply != NULL) {
        reply->setDataPosition(0);
    }

    return err;

服务端会继承BBinder。并实现onTransact()方法对请求进行处理;

整个Binder架构可以用下图概括:
这里写图片描述

ServiceManager的作用图解

这里写图片描述

写在最后
创建一个Binder服务所需的工作:
1.什么时候去启动;
2.在onTransact方法中处理不同的请求,调用不同接口。对外界提供这些接口。服务端需要去实现这些接口;
3.和Binder交互,随时响应Binder的请求。(ServiceManager通过不同循环读取来交互)
4.给客户端提供代理。(客户端获取到BpBinder时,对其进行一个封装,对不同请求相应的处理)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binderAndroidbinde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向进程通信10、使用socket实现一个可以让app执行shell命令的程序

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值