Android FrameWork——Binder機制詳解(2)

來源:互聯網
上載者:User

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通訊的內部機制總算介紹完了,也遺留了不少問題,路過的同仁們若對我提出的這些問題有清楚的也希望分享一下!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.