Android Binder處理序間通訊---註冊Service組件---發送和處理BC_REPLY返回協議

來源:互聯網
上載者:User

標籤:android   style   blog   class   c   code   

本文參考《Android系統原始碼情景分析》,作者羅昇陽

一、測試代碼:

       ~/Android/external/binder/server

        ----FregServer.cpp

        ~/Android/external/binder/common

        ----IFregService.cpp

        ----IFregService.h

       ~/Android/external/binder/client

       ----FregClient.cpp


       Binder庫(libbinder)代碼:

       ~/Android/frameworks/base/libs/binder

       ----BpBinder.cpp

       ----Parcel.cpp

       ----ProcessState.cpp

       ----Binder.cpp

       ----IInterface.cpp

       ----IPCThreadState.cpp

       ----IServiceManager.cpp

       ----Static.cpp

       ~/Android/frameworks/base/include/binder

       ----Binder.h

       ----BpBinder.h

       ----IInterface.h

       ----IPCThreadState.h

       ----IServiceManager.h

       ----IBinder.h

       ----Parcel.h

       ----ProcessState.h


        驅動層代碼:

       ~/Android//kernel/goldfish/drivers/staging/android

       ----binder.c

       ----binder.h


二、源碼分析

      在Android Binder處理序間通訊---註冊Service組件---Client發送BC_TRANSACTIONhttp://blog.csdn.net/jltxgcy/article/details/26076149一文中,在最後線程睡眠以等待處理序間通訊結果。

      在http://blog.csdn.net/jltxgcy/article/details/26076149文章中,我們假設請求註冊Service組件FregService的線程thread正在Binder驅動程式函數binder_thread_read中等待Service Manager完成註冊操作。現在既然Service Manager已經完成了對Service組件FregService的註冊,並且線程thread已經被Binder驅動程式喚醒,接下來它就會執行函數binder_thread_read來處理它的todo隊列中的工作項目了。

      現在請求註冊Service組件FregService的線程thread的todo隊列中有一個類型為BINDER_WORK_TRANSACTION的工作項目,因此,接下來我們就分析函數binder_thread_read處理這個工作項目的過程。

       ~/Android//kernel/goldfish/drivers/staging/android

       ----binder.c

static intbinder_thread_read(struct binder_proc *proc, struct binder_thread *thread,void  __user *buffer, int size, signed long *consumed, int non_block){void __user *ptr = buffer + *consumed;void __user *end = buffer + size;        .........while (1) {uint32_t cmd;struct binder_transaction_data tr;struct binder_work *w;struct binder_transaction *t = NULL;if (!list_empty(&thread->todo))w = list_first_entry(&thread->todo, struct binder_work, entry);//從線程thread的todo隊列中取出這個類型為BINDER_WORK_TRANSACTION的工作項目else if (!list_empty(&proc->todo) && wait_for_proc_work)w = list_first_entry(&proc->todo, struct binder_work, entry);else {..........}.....switch (w->type) {case BINDER_WORK_TRANSACTION: {t = container_of(w, struct binder_transaction, work);//接著又將該工作項目的宿主binder_transaction結構體取回來,並且儲存在變數t中} break;........if (t->buffer->target_node) {//NULL.......} else {tr.target.ptr = NULL;tr.cookie = NULL;cmd = BR_REPLY;}tr.code = t->code;//0tr.flags = t->flags;//0tr.sender_euid = t->sender_euid;if (t->from) {struct task_struct *sender = t->from->proc->tsk;tr.sender_pid = task_tgid_nr_ns(sender, current->nsproxy->pid_ns);} else {tr.sender_pid = 0;}tr.data_size = t->buffer->data_size;//資料緩衝區大小tr.offsets_size = t->buffer->offsets_size;//位移數組大小tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;//核心緩衝區的核心空間地址和使用者空間地址相差一個固定值,並且儲存在它的成員變數user_buffer_offset中tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));//位移儲存在資料緩衝區的後面if (put_user(cmd, (uint32_t __user *)ptr))//將命令返回return -EFAULT;ptr += sizeof(uint32_t);if (copy_to_user(ptr, &tr, sizeof(tr)))//將binder_transaction_data結構體tr返回return -EFAULT;ptr += sizeof(tr);......list_del(&t->work.entry);//刪除該任務項t->buffer->allow_user_free = 1;//允許釋放if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {........} else {t->buffer->transaction = NULL;kfree(t);//釋放binder_transaction結構體t        .........}break;}done:*consumed = ptr - buffer;//cmd和binder_transaction_data結構體tr大小之和.......return 0;}
      首先從線程thread的todo隊列中取出這個類型為BINDER_WORK_TRANSACTION的工作項目,並且儲存在變數w中。接著又將該工作項目的宿主binder_transaction結構體取回來,並且儲存在變數t中。

      然後利用binder_transaction結構體t初始化binder_transaction_data結果體tr。然後將cmdBR_REPLY和tr返回使用者空間。

  

      線程thread執行完成函數binder_thread_read之後,先返回到函數binder_ioctl中,接著再返回IPCThreadState類的成員函數talkWithDriver中,最後返回到IPCThreadState類的成員函數waitForResponse中來處理BR_REPLY返回協議,如下所示:

     ~/Android/frameworks/base/libs/binder

     ----IPCThreadState.cpp

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){    int32_t cmd;    int32_t err;    while (1) {        if ((err=talkWithDriver()) < NO_ERROR) break;        .......                cmd = mIn.readInt32();//cmd為BR_REPLY        ......        switch (cmd) {        .......        case BR_REPLY:            {                binder_transaction_data tr;                err = mIn.read(&tr, sizeof(tr));//從協議緩衝區mIn的內容讀到一個binder_transaction_data結構體tr中                .........                if (reply) {//不為NULL                    if ((tr.flags & TF_STATUS_CODE) == 0) {//tr.flags為0                        reply->ipcSetDataReference(//將儲存在binder_transaction結構體tr中的處理序間通訊結果儲存在Parcel對象reply中                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),                            tr.data_size,                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),                            tr.offsets_size/sizeof(size_t),                            freeBuffer, this);                    } else {                       .......                    }                } else {                   ........                }            }            goto finish;           .........        }    }finish:   .....    return err;}
       首先從返回協議緩衝區mIn中讀取一個BR_REPLY返回協議代碼。然後又從協議緩衝區mIn的內容讀到一個binder_transaction_data結構體tr中。
       如果binder_transaction_data結構體tr的成員變數flags的TF_STATUS_CODE位等於0,就說明當前進程之前所發出的一個處理序間通訊請求已經被成功地處理了。因此,就將儲存在binder_transaction結構體tr中的處理序間通訊結果儲存在Parcel對象reply中,這是通過調用Parcel對象reply的成員函數ipcSetDataReference來實現的。實現如下:

     ~/Android/frameworks/base/libs/binder

     ----Parcel.cpp

void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,    const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie){    freeDataNoInit();    mError = NO_ERROR;    mData = const_cast<uint8_t*>(data);//處理序間通訊結果資料的緩衝區    mDataSize = mDataCapacity = dataSize;處理序間通訊結果資料的緩衝區大小    ..........    mDataPos = 0;    ..........    mObjects = const_cast<size_t*>(objects);//位移數組起始位置    mObjectsSize = mObjectsCapacity = objectsCount;//位移數組大小    .........    mOwner = relFunc;//freeBuffer    mOwnerCookie = relCookie;//當前線程IPCThreadState對象    ........}
      參數data指向了用來儲存處理序間通訊結果資料的緩衝區,它的大小由參數dataSize來描述。參數object指向了一個Binder對象位移數組,用來描述儲存在處理序間通訊資料緩衝區的Binder對象的位置,它的大小由參數objectsCount來描述,參數relFunc是一個函數指標,它指向了IPCTheadState類的成員函數freeBuffer,參數relCookie指向了當前線程的IPCThreadState對象。

      然後依次賦值給Parcel類的不同成員變數,mData是資料緩衝區的其真實位址,mDataSize為資料緩衝區大小,mDataPos為下一個用來讀入的位置。mObjects為位移數組起始地址,mObjetctSize是位移數組mObjects下一個用於讀入資料的位置,mObjectsCapacity為位移數組的大小。

      mOwner儲存著freeBuffer的函數指標,mOwnerCookie儲存者當前線程IPCThreadState對象。線程獲得了儲存在當前Parcel對象中的處理序間通訊結果資料之後,它就會析構該Parcel對象,而該Parcel對象在析構時,會調用它的成員變數mOwner和mOwnerCookie來調用IPCTheadState類的成員函數freeBuffer釋放它內部使用的資料緩衝區mData。由於這個資料緩衝區是在Binder驅動程式中分配的,即它指向一個核心緩衝區,因此IPCThreadState類的成員函數freeBuffer會通過BC_FREE_BUFFER命令協議通知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.