• Android FrameWork——Binder机制详解(2)


    6.前面5个段落我主要说明了BinderProxy是如何把数据发送出去的,Ok, 那么接下来,我们肯定想要知道服务端是怎么接收数据并传递给相应的BBinder进行处理的,有没有注意到前面waitForResponse我标注为蓝 色的代码,这给我们一个启示,也许接收返回数据(进程作为客户端)和接收命令(进程作为服务端)处理的是同一个函数,但这是我的一个猜测,而实际上我参阅 其它blog和代码后并非这么回事,waitForResponse只在客户端发送完数据等待接收数据才被调用的,那么服务端是怎么接收数据的呢?做过 socket编程的同仁们可能知道,服务端为实现接收数据和链接,一般会启动一个监听线程去监听客户端发过来的数据,同样android进程通信的服务端 也是这么做的,在这里我先给你看一个Debug线程图:

    这是一个简单的Android应用进程Debug图,有没有看到两个Binder Thread线程,每个应用都包含Binder Thread线程,不信你可以试试,它就是负责监听来自Binder驱动的消息的,那么这两个线程在什么时候被起来的呢,这又是我的一个疑问,我也不清楚 不同Activity应用的Binder Thread是在哪里被起来的,我后面有待研究,不过System_process进程我倒是清楚它是如何启动这两个线程的,在 init1->system_init() @System_init.cpp函数最后:
        if (proc->supportsProcesses()) {
            LOGI("System server: entering thread pool.\n");
            ProcessState::self()->startThreadPool();//启动Binder监听线程
            IPCThreadState::self()->joinThreadPool();
            LOGI("System server: exiting thread pool.\n");
        }
    IPCThreadState::self()函数前面我们已经讲过了,它是线程绑定对象,ProcessState::self函数我列出如下
    sp<ProcessState> ProcessState::self()
    {
        if (gProcess != NULL) return gProcess;
       
        AutoMutex _l(gProcessMutex);
        if (gProcess == NULL) gProcess = new ProcessState;
        return gProcess;
    }
    显然ProcessState是单例的,也即每个进程拥有一个ProcessState对象,而每个线程拥有一个IPCThreadState对象,关于 ProcessState,IPCThreadState,网上有一篇转高焕堂先生的blog,建议你此时插读一下,我不作详细说明:
    認識Android的ProcessState類別和物件:
    http://www.android1.net/Topic.aspx?BoardID=31&TopicID=1897

                                           (图2:摘自高焕堂先生课件)

    ProcessState负责打开Binder驱动,与Binder驱动进行通信,而IPCStateThread负责每个具体线程IPC数据读写

    7.服务端监听线程的构建
    服务端具体监听线程是怎样构建的的了,前面我只说了是通过ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool();这两个函数创建的,网上很多blog也只是简单说一下,具体 是怎样一个过程呢?我这里进行一下说明:
    void ProcessState::startThreadPool()
    {
        AutoMutex _l(mLock);
        if (!mThreadPoolStarted) {
            mThreadPoolStarted = true;
            spawnPooledThread(true);
        }
    }
    //////////////////////////////////////
    //isMain==true
    void ProcessState::spawnPooledThread(bool isMain)
    {
        if (mThreadPoolStarted) {
            int32_t s = android_atomic_add(1, &mThreadPoolSeq);
            char buf[32];
            sprintf(buf, "Binder Thread #%d", s);
            LOGV("Spawning new pooled thread, name=%s\n", buf);
            sp<Thread> t = new PoolThread(isMain);
            t->run(buf);
        }
    }
    注意线程的创建是在run方法中,run方法并不像java中Thread.run是新线程的工作函数,它仍在当前线程中执行,PoolThread并没有覆盖父类Thread.run方法,因此它执行的是Thread.run方法:
    status_t Thread::run(const char* name, int32_t priority, size_t stack)
    {
     。。。
    //这个时候才真正创建一个新的线程,新线程的工作方法是_threadLoop,它仍是父类Thread的一个方法
            res = createThreadEtc(_threadLoop,
                    this, name, priority, stack, &mThread);
     。。。
    }
    ////////////////////////////////////////////
    //新线程工作方法
    int Thread::_threadLoop(void* user)
    {
    。。。
        do {
    //调用虚函数threadLoop(),该函数被PoolThread实现
                result = self->threadLoop();
            }
    。。。
        } while(strong != 0);   
        return 0;
    }
    ////////////////////////////////////////////
    //PoolThread成员方法
        virtual bool threadLoop()
        {
            IPCThreadState::self()->joinThreadPool(mIsMain);//真正线程工作函数
            return false;
        }
    通过上面的代码我们看出,ProcessState::self()->startThreadPool();创建了一个新的线程,并且新线程的工 作函数 IPCThreadState::self()->joinThreadPool(true);紧接着当前线程又调用了 IPCThreadState::self()->joinThreadPool(true);我这里是以system_process进程为例, 那说明system_process至少有两个binder线程监听Binder驱动消息,接下来我们仔细看一下 joinThreadPool(true)函数的实现:
    //服务端线程工作函数
    void IPCThreadState::joinThreadPool(bool isMain)
    {
        mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);//通知驱动开始消息监听??
       androidSetThreadSchedulingGroup(mMyThreadId, ANDROID_TGROUP_DEFAULT);//加入默认线程组??

       status_t result;

       do {

           int32_t cmd;

           。。。
           // now get the next command to be processed, waiting if necessary

           result = talkWithDriver();

           if (result >= NO_ERROR) {

               size_t IN = mIn.dataAvail();

               if (IN < sizeof(int32_t)) continue;

               cmd = mIn.readInt32();//读取命令

               }
               result = executeCommand(cmd);//执行命令

           }    

           if(result == TIMED_OUT && !isMain) {

               break;

           }

       } while (result != -ECONNREFUSED && result != -EBADF);

       mOut.writeInt32(BC_EXIT_LOOPER);//通知驱动结束消息监听

       talkWithDriver(false);

    }
    通过while循环调用IPCThreadState.mIn读取cmd并执行,这我有一个疑惑就是多个线程去透过IPCThreadState.mIn读取驱动消息会不会存在问题?这个暂且mark一下,以后再分析
    到此,我们总算了解服务端的消息监听机制,证明并不是如段落6我猜测的IPCThreadState.waitForResponse去接收消息的,而是 IPCThreadState::joinThreadPool中直接透过IPCThreadState.mIn读取消息的。

    8.消息处理IPCThreadState::executeCommand(int32_t cmd)
    走到这一步视乎离目标已经不远了,回到我们原来的问题,别让我们身陷代码堆栈而迷失了方向,我们前面做的这些工作,目的都是为了客户端的BpBinder 对象能够调到服务端的BBinder对象,而在BpBinder中有一个mHandle参数指定了服务端的Binder通信地址,因此消息才得以发送到服 务端,现在我们有一个问题要解决就是,服务端可能存在多个BBinder对象,我们接收消息后,如何找到对应的BBinder对象把消息传给它处理了,我 们先看消息处理函数
    executeCommand:
    status_t IPCThreadState::executeCommand(int32_t cmd)
    {
        BBinder* obj;//BBinder对象地址
        RefBase::weakref_type* refs;
        status_t result = NO_ERROR;
       
        switch (cmd) {
        case BR_ERROR:
            ...
            ...
        //这是BC_TRANSACTION消息到服务端对应的cmd(客户端发送的是cmd==BC_TRANSACTION,服务端cmd变成BR_TRANSACTION?待研究)
        case BR_TRANSACTION:
            {
                //构造接收消息结构体,与前面客户端writeTransactionData构造的传输数据包一致的结构体
                binder_transaction_data tr;
                result = mIn.read(&tr, sizeof(tr));//读取消息到结构体tr
                LOG_ASSERT(result == NO_ERROR,
                    "Not enough command data for brTRANSACTION");
                if (result != NO_ERROR) break;
               
                Parcel buffer;
                buffer.ipcSetDataReference(
                    reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),//tr.data.ptr.buffer是消息体,也就是传输参数Parcel
                    tr.data_size,
                    reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                    tr.offsets_size/sizeof(size_t), freeBuffer, this);
               
                const pid_t origPid = mCallingPid;
                const uid_t origUid = mCallingUid;
               
                mCallingPid = tr.sender_pid;//客户端进程pid,uid(uid代表什么??)
                mCallingUid = tr.sender_euid;           
               。。。           
                Parcel reply;           
                if (tr.target.ptr) {//tr.target.ptr值在客户端writeTransactionData中并未设置,只是设置了tr.target.handle = handle;猜测在Binder驱动,根据handle找到了对应BBinder的地址并填写到这个字段了。
                    sp<BBinder> b((BBinder*)tr.cookie);
                    const status_t error = b->transact(tr.code, buffer, &reply, 0);//调用对应的BBinder对象的transact方法
                    if (error < NO_ERROR) reply.setError(error);
                   
                } else {//tr.target.ptr为空,没有指定BBinder对象,调用the_context_object->transact方法,the_context_object具体是什么对象?我不能够搜索到代码
                    const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);//猜测the_context_object应该是ApplicationThread对象,代表本应用进程上下文,不知道是否正确
                    if (error < NO_ERROR) reply.setError(error);
                }
               
                if ((tr.flags & TF_ONE_WAY) == 0) {//默认同步方式,需要发送返回数据
                    LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                    sendReply(reply, 0);//发送返回数据
                } else {
                    LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);//ONEWAY方式客户端并不等待调用返回,因此不需要发送返回数据
                }
               
                mCallingPid = origPid;
                mCallingUid = origUid;

                IF_LOG_TRANSACTIONS() {
                    TextOutput::Bundle _b(alog);
                    alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
                        << tr.target.ptr << ": " << indent << reply << dedent << endl;
                }
               
            }
            break;
       
        case BR_DEAD_BINDER:
         ...
         ...       
       
        }

        ...
        return result;
    }
    尽管还是存在很多疑问,但是大概脉络应该已经清楚了,收到数据包binder_transaction_data tr后根据tr.target.ptr得到BBinder对象指针,然后调用该对象的transact方法。

    9.回到java层的Binder对象
    在图一中,对c层的IBinder类继承结构已有一个清楚的说明,BBinder有两个子类,一个是JavaBBinder,一个是 BnInterface,若Binder存根对象用C实现的,那它会继承BnInterface,以MediaPlayerService为例,它的继承 结构如 下:MediaPlayerService-->BnMediaPlayerService-->BnInterface<IMediaPlayerService>-->BBinder-->IBinder
    代码调用过程图如下

                                                                       (图3)

    这个很简单,再看怎么调用到java层的Binder对象,前面在图1中已经描述 了,java Binder对象对应到C空间的对象是JavaBBinder对象,所以,在BBinder::tansact方法中,调用到得是 JavaBBinder.onTransact方法,在深入JavaBBinder.onTransact前我们先了解一下JavaBBinder是一个 什么对象,它怎么建立与java层Binder对象联系的,下面是JavaBBinder的构造函数:
       //env:java虚拟机指针
      //object:java层的Binder对象
        JavaBBinder(JNIEnv* env, jobject object)
            : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
        {
            LOGV("Creating JavaBBinder %p\n", this);
            android_atomic_inc(&gNumLocalRefs);
            incRefsCreated(env);
        }
    通过JavaBBinder的构造函数,我们可以推测,在构建java层Binder对象时也构造了对应的C层的一个JavaBBinder,JavaBBinder对象有两个成员,
        JavaVM* const   mVM;
        jobject const   mObject;
    显然,JavaBBinder其实就是对java层Binder对象的一个包装对象,理解了这个,我们再看JavaBBinder.onTransact
        virtual status_t onTransact(
            uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
        {
            JNIEnv* env = javavm_to_jnienv(mVM);
           jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
                code, (int32_t)&data, (int32_t)reply, flags);
            jthrowable excep = env->ExceptionOccurred();
            。。。
            return res != JNI_FALSE ? NO_ERROR : UNKNOWN_TRANSACTION;
        }
    我用红色标注了关键代码,可以看到,它通过虚拟机的CallBooleanMethod反向去调用了java层的Binder对象mObject的一个方法,该方法由函数指针gBinderOffsets.mExecTransact引用,我们看一下该函数指针的定义:
        gBinderOffsets.mExecTransact
            = env->GetMethodID(clazz, "execTransact", "(IIII)Z");
    总算找到了execTransact方法,总算浮出水面到了我们熟悉的java层,我就不详细解读代码了,画个调用图如下:

     

                                                                                           (图4)

    到此,Binder通信的内部机制总算介绍完了,也遗留了不少问题,路过的同仁们若对我提出的这些问题有清楚的也希望分享一下!

  • 相关阅读:
    H5实现魔方游戏
    T-SQL:CTE用法(十)
    c# API接收Base64转图片
    T-SQL :联接查询练习 (杂)
    T-SQL:基础练习(杂)
    UI5-文档-导航栏
    UI5-文档-4.10-Descriptor for Applications
    UI5-文档-4.9-Component Configuration
    UI5-文档-4.8-Translatable Texts
    UI5-文档-4.7-JSON Model
  • 原文地址:https://www.cnblogs.com/xiaoxiaoboke/p/2342891.html
Copyright © 2020-2023  润新知