Binder系列6 Binder传输原理之请求处理

一 Binder驱动事务读取

在上一篇文章中,我们知道 Binder 驱动生成了一个 binder_transaction 事务,并把这个事务插入到了目标进程或目标线程的 todo 队列中,并唤醒了目标进程或目标线程.那么目标端是怎么处理的呢?

先说个大概流程:Binder 驱动唤醒目标进程或目标线程后,目标线程会在读取函数中休眠等待的地方醒过来,然后到自己的 todo 队列上把这个事务摘下来,然后分析这个事务,根据事务中携带的数据信息,找到目标 Binder 实体,最后把数据传递给目标Binder实体.这个只是大概的流程,接下来我们详细分析.

1.1 目标线程被唤醒

我们知道负责与 Binder 通信的都是一个个具体的线程, 当目标进程被唤醒时,实际上唤醒的是该目标进程中的某一个 Binder 线程,这个 Binder 线程正常的情况下会通过调用 binder_thread_read() 函数来读取请求者的数据,如果暂时还没有数据传输过来的时候,就会休眠阻塞在这个函数中;而如果有数据传输过来的话,则会在休眠阻塞的地方被唤醒,然后接着处理传输过来的数据,我们来看这个函数:

static int binder_thread_read(struct binder_proc *proc,
			      struct binder_thread *thread,
			      binder_uintptr_t binder_buffer, size_t size,
			      binder_size_t *consumed, int non_block)
{
   
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;
	........
	//检查本线程内部的todo队列中是否有待处理的事务,如果没有,那就等待处理本进程todo队列的work
	wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
	........
	thread->looper |= BINDER_LOOPER_STATE_WAITING;//设置线程looper等待标志
	........
	if (non_block) {
   
		........
	} else {
   
	    //重要函数,休眠阻塞等待,被唤醒后开始往下执行
		ret = binder_wait_for_work(thread, wait_for_proc_work);
	}
   //走到这里,证明已经被唤醒了,结束等待,需要去掉线程looper的等待状态
	thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
	........
	while (1) {
   
		uint32_t cmd;
		struct binder_transaction_data tr;
		struct binder_work *w = NULL;
		struct list_head *list = NULL;
		struct binder_transaction *t = NULL;
		struct binder_thread *t_from;

		binder_inner_proc_lock(proc);
		//优先处理本线程内部的 todo 队列,如果为空,则处理进程的 todo 队列
		if (!binder_worklist_empty_ilocked(&thread->todo))
			list = &thread->todo;//选取线程的todo
		else if (!binder_worklist_empty_ilocked(&proc->todo) &&
			   wait_for_proc_work)
			list = &proc->todo;//选取进程的todo
		else {
   
			//若无数据且当前线程looper_need_return为false, 则重试
			if (ptr - buffer == 4 && !thread->looper_need_return)
				goto retry;
			break;
		}
		........
		//选取好todo队列后,开始从todo队列中摘除待处理的binder_transaction事务
		w = binder_dequeue_work_head_ilocked(list);//从list队列中取出第一项work
		if (binder_worklist_empty_ilocked(&thread->todo))
     		//如果线程的todo为空,则设置process_todo false
			thread->process_todo = false;

		switch (w->type) {
   //判断binder_work的类型,这里为BINDER_WORK_TRANSACTION
		case BINDER_WORK_TRANSACTION: {
   
			//根据work找到对应的binder_transaction事务
			t = container_of(w, struct binder_transaction, work);
		} break;
		........
		}//end switch

		if (!t)
			continue;
		//以上我们已经拿到从请求端发送过来的binder_transaction事务
		//接下来解析这个事务
		if (t->buffer->target_node) {
   //是否存在目标节点,这里为存在
			struct binder_node *target_node = t->buffer->target_node;
			struct binder_priority node_prio;
            //非常重要,把Binder实体的地址赋值给tr的target.ptr
			tr.target.ptr = target_node->ptr;
			//非常重要,Binder实体的地址赋值给tr的target.cookie
			tr.cookie =  target_node->cookie;
			........
			cmd = BR_TRANSACTION;//构建BR_TRANSACTION命令把tr数据返回到用户空间
		} else {
   
			tr.target.ptr = 0;
			tr.cookie = 0;
			cmd = BR_REPLY;
		}
		//事务的信息全部赋值给tr这个binder_transaction_data结构,用来返回到用户空间
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
		t_from = binder_get_txn_from(t);//获取事务t的from源线程
		........
		tr.data_size = t->buffer->data_size;
		tr.offsets_size = t->buffer->offsets_size;
		// 使tr中的数据缓冲区和偏移数组的地址值指向事务t中的数据缓冲区和偏移数组的地址值
		tr.data.ptr.buffer = (binder_uintptr_t)
			((uintptr_t)t->buffer->data +
			binder_alloc_get_user_buffer_offset(&proc->alloc));
		tr.data.ptr.offsets = tr.data.ptr.buffer +
					ALIGN(t->buffer->data_size,
					    sizeof(void *));
     //将返回协议BR_TRANSACTION拷贝到目标线程提供的用户空间
		if (put_user(cmd, (uint32_t __user *)ptr)) {
   
			........
		}
		ptr += sizeof(uint32_t);
		// 将binder_transaction_data类型的tr拷贝到目标线程提供的用户空间
		if (copy_
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值