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系統源代碼情景分析》,作者羅升陽

一、service manager代碼:

~/Android/frameworks/base/cmd/servicemanager ----binder.c ----service_manager.c ----binder.h


驅動層代碼:

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

----binder.c

----binder.h


二、源碼分析

從Android Binder進程間通信---注冊Service組件---Server處理BC_TRANSACTIONhttp://blog.csdn.net/jltxgcy/article/details/26151113,我們已經知道Service Manager成功地將一個Service組件注冊到內部的Service組件列表所svclist中之後,接著就會調用函數binder_send_reply將Service組件注冊結果返回給Binder驅動程序,Binder驅動程序再將該結果返回給請求注冊Service組件的進程。

~/Android/frameworks/base/cmd/servicemanager

----binder.c

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       void *buffer_to_free,
                       int status)  //status為0,注冊成功代碼0寫入binder_io結構體reply中
{
    struct {
        uint32_t cmd_free;
        void *buffer;
        uint32_t cmd_reply;
        struct binder_txn txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER;//BC_FREE_BUFFER後面跟的通信數據是一個內核緩沖區的用戶空間地址
    data.buffer = buffer_to_free;//一個用戶空間地址,指向一塊用來傳輸進程間通信數據的內核緩沖區
    data.cmd_reply = BC_REPLY;//BC_REPLY後面跟的通信數據是一個binder_transaction_data結構體,即一個binder_txn結構體
    data.txn.target = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {//status為0
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offs_size = 0;
        data.txn.data = &status;
        data.txn.offs = 0;
    } else {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;//0的大小,因為做為do_add_service成功,reply結構體放入0
        data.txn.offs_size = ((char*) reply->offs) - ((char*) reply->offs0);//0
        data.txn.data = reply->data0;//指向了0
        data.txn.offs = reply->offs0;//無
    }
    binder_write(bs, &data, sizeof(data));
}
首先定義了一個匿名結構體data,用來描述一個BC_FREE_BUFFER和一個BC_REPLY命令協議,分別用成員變量cmd_free和cmd_reply來表示。命令協議BC_FREE_BUFFER後面跟的通信數據是一個內核緩沖區的用戶空間地址,它就保存在成員變量buffer中;而命令協議BC_REPLY後面跟的通信數據是一個binder_transaction_data結構體,即一個binder_txn結構體,它就保存在成員變量txn中。

然後調用binder_write將匿名結構體data中BC_FREE_BUFFER和BC_REPLY命令協議發送給Binder驅動程序。實現如下:

~/Android/frameworks/base/cmd/servicemanager

----binder.c

int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;//匿名結構體data指針
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
函數binder_write是通過IO控制命令BINDER_WRITE_READ來將BC_FREE_BUFFER和BC_REPLY命令協議發送給Binder驅動程序的,映射到驅動程序binder_thread_write。

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

----binder.c

int
binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
		    void __user *buffer, int size, signed long *consumed)
{
	uint32_t cmd;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	while (ptr < end && thread->return_error == BR_OK) {
		if (get_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
                ......
		case BC_TRANSACTION:
		case BC_REPLY: {
			struct binder_transaction_data tr;

			if (copy_from_user(&tr, ptr, sizeof(tr)))//上面剛提到的binder_txn結構體data.txn
				return -EFAULT;
			ptr += sizeof(tr);
			binder_transaction(proc, thread, &tr, cmd == BC_REPLY);//tr為上面已經賦值的data.txn
			break;
		}
                ........
		default:
			printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid, thread->pid, cmd);
			return -EINVAL;
		}
		*consumed = ptr - buffer;
	}
	return 0;
}
我們暫時不分析BC_FREE_BUFFER命令,只分析BC_REPLY,while第二次循環會執行到這裡。
tr就是上面已經賦值的data.txn。然後調用binder_transaction函數,實現如下:

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

----binder.c

static void
binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
	struct binder_transaction_data *tr, int reply)
{
	struct binder_transaction *t;
	struct binder_work *tcomplete;
	......
	struct binder_proc *target_proc;
	struct binder_thread *target_thread = NULL;
	struct binder_node *target_node = NULL;
	struct list_head *target_list;
	wait_queue_head_t *target_wait;
	struct binder_transaction *in_reply_to = NULL;
	........
	uint32_t return_error;

	........

	if (reply) {
		in_reply_to = thread->transaction_stack;//首先從線程thread的事務堆棧中將該binder_transaction結構體取出來,並且保存在變量in_reply_to中
		if (in_reply_to == NULL) {
			......
			return_error = BR_FAILED_REPLY;
			goto err_empty_call_stack;
		}
		binder_set_nice(in_reply_to->saved_priority);
		if (in_reply_to->to_thread != thread) {
			........
			return_error = BR_FAILED_REPLY;
			in_reply_to = NULL;
			goto err_bad_call_stack;
		}
		thread->transaction_stack = in_reply_to->to_parent;
		target_thread = in_reply_to->from;
		if (target_thread == NULL) {
			return_error = BR_DEAD_REPLY;
			goto err_dead_binder;
		}
		if (target_thread->transaction_stack != in_reply_to) {
			.........
			return_error = BR_FAILED_REPLY;
			in_reply_to = NULL;
			target_thread = NULL;
			goto err_dead_binder;
		}
		target_proc = target_thread->proc;//找到了目標進程
	} else {
		........
	}
	if (target_thread) {
		.........
		target_list = &target_thread->todo;//分別將它的todo隊列和wait等待隊列作為目標todo隊列target_list和目標wait等待隊列target_wait
		target_wait = &target_thread->wait;//分別將它的todo隊列和wait等待隊列作為目標todo隊列target_list和目標wait等待隊列target_wait
	} else {
		.........
	}
	.........

	/* TODO: reuse incoming transaction for reply */
	t = kzalloc(sizeof(*t), GFP_KERNEL);//分配了binder_transaction結構體
	........

	tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);//分配了binder_work結構體
	if (tcomplete == NULL) {
		return_error = BR_FAILED_REPLY;
		goto err_alloc_tcomplete_failed;
	}
	.......

	if (!reply && !(tr->flags & TF_ONE_WAY))
		t->from = thread;//service_manager的主線程
	else
		t->from = NULL;
	t->sender_euid = proc->tsk->cred->euid;//service_manager進程號
	t->to_proc = target_proc;//目標進程
	t->to_thread = target_thread;//目標線程
	t->code = tr->code;//0
	t->flags = tr->flags;//0
	t->priority = task_nice(current);
	t->buffer = binder_alloc_buf(target_proc, tr->data_size,
		tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));//分配了binder_buffer結構體
	if (t->buffer == NULL) {
		return_error = BR_FAILED_REPLY;
		goto err_binder_alloc_buf_failed;
	}
	t->buffer->allow_user_free = 0;//不允許釋放
	.......
	t->buffer->transaction = t;
	t->buffer->target_node = target_node;//NULL
	if (target_node)
		binder_inc_node(target_node, 1, 0, NULL);//增加目標Binder實體對象的強引用計數

	offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));//偏移數組在data中起始位置,位於數據緩沖區之後

	if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {//數據緩沖區拷貝到data中
		binder_user_error("binder: %d:%d got transaction with invalid "
			"data ptr\n", proc->pid, thread->pid);
		return_error = BR_FAILED_REPLY;
		goto err_copy_data_failed;
	}
	if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {//偏移數組拷貝到data中,偏移數組位於數據緩沖區之後
		binder_user_error("binder: %d:%d got transaction with invalid "
			"offsets ptr\n", proc->pid, thread->pid);
		return_error = BR_FAILED_REPLY;
		goto err_copy_data_failed;
	}
	if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
		binder_user_error("binder: %d:%d got transaction with "
			"invalid offsets size, %zd\n",
			proc->pid, thread->pid, tr->offsets_size);
		return_error = BR_FAILED_REPLY;
		goto err_bad_offset;
	}
	off_end = (void *)offp + tr->offsets_size;
	for (; offp < off_end; offp++) {//偏移數組裡面沒有內容
		.....
	}
	if (reply) {
		BUG_ON(t->buffer->async_transaction != 0);
		binder_pop_transaction(target_thread, in_reply_to);//TODO
	} else if (!(t->flags & TF_ONE_WAY)) {
		.........
	} else {
		.........
	}
	t->work.type = BINDER_WORK_TRANSACTION;
	list_add_tail(&t->work.entry, target_list);//加入到目標線程的todo
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	list_add_tail(&tcomplete->entry, &thread->todo);//加入到本線程的todo
	if (target_wait)
		wake_up_interruptible(target_wait);//喚醒目標線程
	return;
}
當Binder驅動程序分發一個進程間通信請求給一個線程處理時,就會將一個binder_transaction結構體壓入到它的事務堆棧中,因此首先從線程thread的事務堆棧中將該binder_transaction結構體取出來,並且保存在變量in_reply_to中。

binder_transaction結構體in_reply_to成員變量from指向了之前請求與thread進行進程間通信的線程,因此緊接著獲取了目標線程target_thread。

找到目標線程target_thread之後,分別將它的todo隊列和wait等待隊列作為目標todo隊列target_list和目標wait等待隊列target_wait。
然後使用初始化binder_transaction結構體t,加入到目標線程的todo。又初始化了binder_work結構體,加入到本線程(service_manager主線程)的todo隊列。最後喚醒目標線程。

我們假設本線程繼續執行,執行完畢後再執行被喚醒的目標線程。

service_manager主線程繼續執行,執行完binder_transaction,一層一層的返回,最終返回到binder_loop中,繼續執行for循環,ioctl映射到binder_ioctl,由於只有read_size大於0,所以執行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;//結束位置

	int ret = 0;
	int wait_for_proc_work;

	if (*consumed == 0) {
		if (put_user(BR_NOOP, (uint32_t __user *)ptr))//BR_NOOP存入剛才的局部變量中
			return -EFAULT;
		ptr += sizeof(uint32_t);
	}

retry:
	wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);//wait_for_proc_work目前為0,表示線程有要處理的任務

	if (thread->return_error != BR_OK && ptr < end) {
		..........
	}


	thread->looper |= BINDER_LOOPER_STATE_WAITING;//looper為BINDER_LOOPER_STATE_ENTERED,BINDER_LOOPER_STATE_WAITING
	if (wait_for_proc_work)//為0
		proc->ready_threads++;
	mutex_unlock(&binder_lock);
	if (wait_for_proc_work) {//為0
	         ........
	} else {
		if (non_block) {//非阻塞要立刻返回處理結果
			if (!binder_has_thread_work(thread))有任務就接下往下執行,沒有任務就返回
				ret = -EAGAIN;
		} else
			ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));//有任務不睡眠,繼續往下執行
	}
	mutex_lock(&binder_lock);
	if (wait_for_proc_work)//為0
		proc->ready_threads--;
	thread->looper &= ~BINDER_LOOPER_STATE_WAITING;//looper為BINDER_LOOPER_STATE_ENTERED

	if (ret)
		return ret;

	while (1) {
		case BINDER_WORK_TRANSACTION_COMPLETE: {
			cmd = BR_TRANSACTION_COMPLETE;
			if (put_user(cmd, (uint32_t __user *)ptr))//將一個BR_TRANSACTION_COMPLETE返回協議寫入到用戶提供的緩沖區。
				return -EFAULT;
			ptr += sizeof(uint32_t);


			binder_stat_br(proc, thread, cmd);
			if (binder_debug_mask & BINDER_DEBUG_TRANSACTION_COMPLETE)
				printk(KERN_INFO "binder: %d:%d BR_TRANSACTION_COMPLETE\n",
				       proc->pid, thread->pid);


			list_del(&w->entry);//刪除todo上的工作項
			kfree(w);//釋放結構體
			binder_stats.obj_deleted[BINDER_STAT_TRANSACTION_COMPLETE]++;
		} break;
	}

done:

	*consumed = ptr - buffer;//消耗的大小
	..........
	return 0;
}

執行完binder_thread_read,返回binder_ioctl,最後返回binder_loop函數,開始執行binder_parse,實現如下:

~/Android/frameworks/base/cmd/servicemanager

----binder.c

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)//ptr為BR_TRANSACTION_COMPLETE的指針,size為它的大小
{
    int r = 1;
    uint32_t *end = ptr + (size / 4);

    while (ptr < end) {
        uint32_t cmd = *ptr++;
        .......
        switch(cmd) {//cmd為BR_TRANSACTION_COMPLETE
        ......
         case BR_TRANSACTION_COMPLETE:
            break;
        ......}
    }

    return r;
}
執行完binder_parse後,繼續執行binder_loop的for循環,又一次睡眠等待直到其所屬的進程有新的未處理項為止,停留在下面的代碼:

wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));//睡眠等待直到其所屬的進程有新的未處理項為止

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