当前位置: 代码迷 >> Android >> Android Binder进程间通信-登记Service组件-发送和处理BC_REPLY返回协议
  详细解决方案

Android Binder进程间通信-登记Service组件-发送和处理BC_REPLY返回协议

热度:36   发布时间:2016-04-28 05:34:54.0
Android Binder进程间通信---注册Service组件---发送和处理BC_REPLY返回协议

本文参考《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;//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/include/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/include/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驱动程序将内核缓冲区释放掉。

  相关解决方案