Android 8.0系统源码分析--Binder进程间通信(二)

本文深入分析Android 8.0系统中Binder进程间通信的细节,从Application到Kernel,涉及IPCThreadState、binder驱动、binder_transaction函数等关键组件,揭示Binder通信的核心流程。
摘要由CSDN通过智能技术生成

     今天下午去下棋,好久没下了,都没啥感觉了,不过还是一直胜,因为对手还不够我的等级,哈哈哈哈。周末在家,陪老爸老妈吃午饭,聊聊天,感觉这就是最实在的生活,可惜老婆和臭蛋不在身边,一家人都一起出去逛逛就好了。行了,不多说了,进入我们的正题。

     上节我们分析了Binder通信中从Application开始,是如何通过JNI进入到Native层,最后我们来的Kernal的门前,IPCThreadState、ProcessState两个Binder的适配器为我们准备好了所有的工作,下面就会通过 ioctl 系统调用进入到内核当中,我们接着上一节继续前行。IPCThreadState文件中的waitForResponse方法首先调用talkWithDriver来和Binder驱动进行交互。talkWithDriver方法也是实现在IPCThreadState文件中,该文件的路径为 frameworks\native\libs\binder\IPCThreadState.cpp,talkWithDriver方法的源码如下:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }

    binder_write_read bwr;

    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }

    IF_LOG_COMMANDS() {
        TextOutput::Bundle _b(alog);
        if (outAvail != 0) {
            alog << "Sending commands to driver: " << indent;
            const void* cmds = (const void*)bwr.write_buffer;
            const void* end = ((const uint8_t*)cmds)+bwr.write_size;
            alog << HexDump(cmds, bwr.write_size) << endl;
            while (cmds < end) cmds = printCommand(alog, cmds);
            alog << dedent;
        }
        alog << "Size of receive buffer: " << bwr.read_size
            << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
    }

    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(__ANDROID__)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
        IF_LOG_COMMANDS() {
            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
        }
    } while (err == -EINTR);

    IF_LOG_COMMANDS() {
        alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
            << bwr.write_consumed << " (of " << mOut.dataSize()
                        << "), read consumed: " << bwr.read_consumed << endl;
    }

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        IF_LOG_COMMANDS() {
            TextOutput::Bundle _b(alog);
            alog << "Remaining data size: " << mOut.dataSize() << endl;
            alog << "Received commands from driver: " << indent;
            const void* cmds = mIn.data();
            const void* end = mIn.data() + mIn.dataSize();
            alog << HexDump(cmds, mIn.dataSize()) << endl;
            while (cmds < end) cmds = printReturnCommand(alog, cmds);
            alog << dedent;
        }
        return NO_ERROR;
    }

    return err;
}
     我们当前的场景下是调用AMS去启动Activity的,这里的doReceive的值应该为true,表示需要接收数据,查看了老罗的博客上,也是说值为true,但是从逻辑上还是没看明白,waitForResponse方法中调用时没有传值,这里为什么会是true呢?如果有弄懂的朋友,请指教一下。好了,我们继续往下分析,第一句if (mProcess->mDriverFD <= 0)就是检查参数的合法性,mProcess就是一个类型为ProcessState的成员变量,它是在IPCThreadState的构造方法中传入的,当我们的应用进程启动时,会在ProcessState类的构造方法中调用open_driver,传入的const char *driver常量指针就是"/dev/binder",成功打开binder文件节点后,得到的文件描述符就会赋值为ProcessState类的成员变量mDriverFD。参数检查合法后,就定义一个binder_write_read结构体bwr,它是承载我们传入到内核中的数据载体,它也是定义在binder.h头文件中。接着调用bwr.read_buffer = (uintptr_t)mIn.data() 将我们要传入的参数赋值给bwr的成员变量read_buffer,赋值工作完成后,继续判断bwr的write_size、read_size成员变量是否都等于0,如果是,那就说明不需要作什么工作,直接返回NO_ERROR;如果否,则调用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) 开始和内核通信。

     此处的ioctl就会由内核中的binder驱动来处理,binder驱动中定义的操作函数结构体为file_operations binder_fops,定义在binder.c文件中,源码如下:

static const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};
     我们要跟踪的就是binder_ioctl函数,它的实现源码如下:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
	struct binder_proc *proc = filp->private_data;
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;

	/*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/

	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		return ret;

	mutex_lock(&binder_lock);
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	switch (cmd) {
	case BINDER_WRITE_READ: {
		struct binder_write_read bwr;
		if (size != sizeof(struct binder_write_read)) {
			ret = -EINVAL;
			goto err;
		}
		if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
			ret = -EFAULT;
			goto err;
		}
		binder_debug(BINDER_DEBUG_READ_WRITE,
			     "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
			     proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,
			     bwr.read_size, bwr.read_buffer);

		if (bwr.write_size > 0) {
			ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
			if (ret < 0) {
				bwr.read_consumed = 0;
				if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
					ret = -EFAULT;
				goto err;
			}
		}
		if (bwr.read_size > 0) {
			ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
			if (!list_empty(&proc->todo))
				wake_up_interruptible(&proc->wait);
			if (ret < 0) {
				if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
					ret = -EFAULT;
				goto err;
			}
		}
		binder_debug(BINDER_DEBUG_READ_WRITE,
			     "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
			     proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
			     bwr.read_consumed, bwr.read_size);
		if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
			ret = -EFAULT;
			goto err;
		}
		break;
	}
	case BINDER_SET_MAX_THREADS:
		if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
			ret = -EINVAL;
			goto err;
		}
		break;
	case BINDER_SET_CONTEXT_MGR:
		if (binder_context_mgr_node != NULL) {
			printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
			ret = -EBUSY;
			goto err;
		}
		if (binder_context_mgr_uid != -1) {
			if (binder_context_mgr_uid != current->cred->euid) {
				printk(KERN_ERR "binder: BINDER_SET_"
				       "CONTEXT_MGR bad uid %d != %d\n",
				       current->cred->euid,
				       binder_context_mgr_uid);
				ret = -EPERM;
				goto err;
			}
		} else
			binder_context_mgr_uid = current->cred->euid;
		binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
		if (binder_context_mgr_node == NULL) {
			ret = -ENOMEM;
			goto err;
		}
		binder_context_mgr_node->local_weak_refs++;
		binder_context_mgr_node->local_strong_refs++;
		binder_context_mgr_node->has_strong_ref = 1;
		binder_context_mgr_node->has_weak_ref = 1;
		break;
	case BINDER_THREAD_EXIT:
		binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n",
			     proc->pid, thread->pid);
		binder_free_thread(proc, thread);
		thread = NULL;
		break;
	case BINDER_VERSION:
		if (size != sizeof(struct binder_version)) {
			ret = -EINVAL;
			goto err;
		}
		if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) {
			ret = -EINVAL;
			goto err;
		}
		break;
	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
err:
	if (thread)
		thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
	mutex_unlock(&binder_lock);
	wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret && ret != -ERESTARTSYS)
		printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
	return ret;
}
     binder_proc描述的就是当前主动调用的进程信息,cmd指我们从IPCThreadState中执行talkWithDriver时传入的binder命令协议,值为BINDER_WRITE_READ,往下会涉及到非常多的结构体,如果大家想了解清楚这些结构体,可以去  Android 8.0系统源码分析--开篇 博客中下载 《Android系统源代码情景分析【罗升阳著】》电子书,当中有非常详细的讲解。wait_event_interruptible是一个内核函数,它在当条件成立时将当前进程的状态设置成TASK_INTERRUPTIBLE,然后调用schedule(),而schedule()会将位于TASK_INTERRUPTIBLE状态的当前进程从runqueue队列中删除。从runqueue队列中删除的结果是,当前这个进程将不再参与调度,而当唤醒条件满足时,又调用wake_up()重新将当前进程加入到runqueue队列中,然后该进程就可以继续被调度了。我们当前传入的binder命令协议为BINDER_WRITE_READ,所以就进入到第一个case分支中继续处理,调用copy_from_user内核函数将用户空间传递进来的数据拷贝到该case分支中定义的一个临时变量bwr中,如果拷贝失败,则退出执行,在我们当前的场景下,bwr.read_size为0,因为我们从应用层进来,没有需要读取的数据,而所有的调用参数全部封装在bwr.write_size写入参数中了,所以接下来我们重点关注if (bwr.write_size > 0) 分支,这里调用binder_thread_write函数来继续处理。

     binder_thread_write函数的实现也是在binder.c文件中,该函数中就是case分支对不同场景下的binder命令协议进行处理,函数代码非常长,在IPCThreadState中调用writeTransactionData方法封装参数时,最后是调用mOut.writeInt32(cmd)来往输出参数中写入cmd命令的,此时写入的命令就是BC_TRANSACTION,表示我们当前要进行binder通信,所以下面我们只关注BC_TRANSACTION分支,源码中该分支的实现也非常清晰,先调用copy_from_user拷贝用户数据,然后调用binder_transaction函数进一步处理。

     binder_transaction函数的实现也是在binder.c文件中,该函数就是binder通信中最重要的实现了。该函数的完整源码如下:

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;
	size_t *offp, *off_end;
	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;
	struct binder_transaction_log_entry *e;
	uint32_t return_error;

	e = binder_transaction_log_add(&binder_transaction_log);
	e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
	e->from_proc = proc->pid;
	e->from_thread = thread->pid;
	e->target_handle = tr->target.handle;
	e->data_size = tr->data_size;
	e->offsets_size = tr->offsets_size;

	if (reply) {
		in_reply_to = thread->transaction_stack;
		if (in_reply_to == NULL) {
			binder_user_error("binder: %d:%d got reply transaction "
					  "with no transaction stack\n",
					  proc->pid, thread->pid);
			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) {
			binder_user_error("binder: %d:%d got reply transaction "
				"with bad transaction stack,"
				" transaction %d has target %d:%d\n",
				proc->pid, thread->pid, in_reply_to->debug_id,
				in_reply_to->to_proc ?
				in_reply_to->to_proc->pid : 0,
				i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红-旺永福

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值