Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Binder進程間通信---注冊Service組件---發送和處理BC_REPLY返回協議

Android Binder進程間通信---注冊Service組件---發送和處理BC_REPLY返回協議

編輯:關於Android編程

本文參考《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 int
binder_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;//0
		tr.flags = t->flags;//0
		tr.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(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast(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(data);//進程間通信結果數據的緩沖區
    mDataSize = mDataCapacity = dataSize;進程間通信結果數據的緩沖區大小
    ..........
    mDataPos = 0;
    ..........
    mObjects = const_cast(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驅動程序將內核緩沖區釋放掉。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved