Android 13 - binder阅读(5)- 使用ServiceManager注册服务2

上一篇笔记我们看到了binder_transaction,这个方法很长,这一篇我们将把这个方法拆分开来看binder_transaction做了什么,从而学习binder是如何跨进程通信的。

1 binder_transaction

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply,
			       binder_size_t extra_buffers_size)

首先我们来看函数的参数,第一个参数proc为调用进程的binder_proc(也就是MediaServer进程所有的binder_proc);第二个参数thread我们这里不讨论,主要用于多任务通信;第三个参数tr为从用户空间拷贝过来的binder_transaction_data

函数一开始声明了一串变量,我们挑出一部分给出注释:

	// 目标进程的binder_proc 
	struct binder_proc *target_proc = NULL;
	// 目标进程的binder_node 
	struct binder_node *target_node = NULL;
	// 上下文(servicemanager)
	struct binder_context *context = proc->context;
	// 用户空间数据指针
	const void __user *user_buffer = (const void __user *)
				(uintptr_t)tr->data.ptr.buffer;

由于我们这边传递的cmd为BC_TRANSACTION,所以这里先不看reply分支的内容:

		// 1. 判断target handle是否为0,如果不为0则进入以下分支
		if (tr->target.handle) {
			struct binder_ref *ref;
			// 2. 查找binder_ref
			ref = binder_get_ref_olocked(proc, tr->target.handle,
						     true);
			if (ref) {
				target_node = binder_get_node_refs_for_txn(
						ref->node, &target_proc,
						&return_error);
			} 
		} else {
		// 3. 如果target handle为0,如果不为0则target为servicemanager
			// servicemanager的binder_node可以从proc->context中获得
			target_node = context->binder_context_mgr_node;
			if (target_node)
				target_node = binder_get_node_refs_for_txn(
						target_node, &target_proc,
						&return_error);
		}

以上代码是根据Handle查找目标进程的binder_node,这里分了两种情况:

  1. handle为0,说明目标进程为ServiceManager,binder_context 存储的binder_node即为ServiceManager进程所属的。
  2. handle不为0,说明是其他进程,这里要引出一个新的结构体变量binder_ref,这个东西在我们每次从servicemanager获取服务代理时,由binder驱动帮助我们创建,binder_ref用于存储服务的进程信息,除此之外binder驱动为该代理创建一个handle也存储在binder_ref中,有了这个handle就可以找到目标进程了,最后把创建的binder_ref存储在调用进程的binder_proc的一个红黑树列表中。
    这里讲的可能比较拗口,用一张图来表示下:
    请添加图片描述

我们还是回到handle为0的情况,调用servicemanager注册服务的流程中。接下来这部分我没有仔细去研究,没太看懂,只能写一些我的猜测在这里,大概就是从用户空间拷贝我们一开始打包的Parcel数据到目标进程的共享内存当中:

	struct binder_transaction *t;
	t = kzalloc(sizeof(*t), GFP_KERNEL);
	t->to_proc = target_proc;
	t->to_thread = target_thread;
	t->code = tr->code;
	t->flags = tr->flags;
	t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
		tr->offsets_size, extra_buffers_size,
		!reply && (t->flags & TF_ONE_WAY), current->tgid);
	t->buffer->transaction = t;
	t->buffer->target_node = target_node;
	t->buffer->clear_on_free = !!(t->flags & TF_CLEAR_BUF);
	if (binder_alloc_copy_user_to_buffer(
				&target_proc->alloc,
				t->buffer,
				ALIGN(tr->data_size, sizeof(void *)),
				(const void __user *)
					(uintptr_t)tr->data.ptr.offsets,
				tr->offsets_size)) {
	}

仅仅做完拷贝还没结束,还需要对拷贝的buffer中的一些特殊内容做一些处理,我这里只看是如何处理传递过来的binder对象的:

		hdr = &object.hdr;
		off_min = object_offset + object_size;
		switch (hdr->type) {
		case BINDER_TYPE_BINDER:
		case BINDER_TYPE_WEAK_BINDER: {
			struct flat_binder_object *fp;

			fp = to_flat_binder_object(hdr);
			ret = binder_translate_binder(fp, t, thread);
		} break;
		case BINDER_TYPE_HANDLE:
		case BINDER_TYPE_WEAK_HANDLE: {
			struct flat_binder_object *fp;
			fp = to_flat_binder_object(hdr);
			ret = binder_translate_handle(fp, t, thread);
		} break;

这里主要看binder_translate_binder是如何translate处理的:

static int binder_translate_binder(struct flat_binder_object *fp,
				   struct binder_transaction *t,
				   struct binder_thread *thread)
{
	struct binder_node *node;
	struct binder_proc *proc = thread->proc;
	struct binder_proc *target_proc = t->to_proc;
	struct binder_ref_data rdata;
	int ret = 0;
	// 1 从调用进程中查找要传递的binder对象的binder_node
	node = binder_get_node(proc, fp->binder);
	if (!node) {
		// 2 如果没有查找到,为调用进程创建一个binder_node
		node = binder_new_node(proc, fp);
		if (!node)
			return -ENOMEM;
	}
	// 3 查找目标进程是否已经存储有这个binder_ref,如果没有则为其创建一个binder_ref
	ret = binder_inc_ref_for_node(target_proc, node,
			fp->hdr.type == BINDER_TYPE_BINDER,
			&thread->todo, &rdata);
	if (ret)
		goto done;
	// 将binder实体转换为了引用
	if (fp->hdr.type == BINDER_TYPE_BINDER)
		fp->hdr.type = BINDER_TYPE_HANDLE;
	else
		fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
	fp->binder = 0;
	fp->handle = rdata.desc;
	fp->cookie = 0;
done:
	binder_put_node(node);
	return ret;
}

这里主要做了如下几件事情:

  1. 从调用进程中查找要传递的binder对象的binder_nodebinder_proc中同样有一个红黑树链表存储有该进程中的binder_node对象;
  2. 如果没有查找到,为调用进程创建一个binder_node,说明传递的对象是属于调用进程的;
  3. 查找目标进程是否已经有了binder_node对应的binder_ref,如果没有则说明目标进程还没有这个要传递的binder对象的引用,为目标进程创建一个binder_ref引用(handle),存储到flat_binder_object 中写到共享内存当中。

binder驱动会将我们传递的binder实体转化为属于调用进程的binder_node,并且提供一个binder_ref给目标进程,目标进程持有的永远都是代理。
请添加图片描述
到这里,binder数据的传输就结束了。


总结:这一篇笔记写的比较水,最重要的共享内存并没有理解清楚,有想了解的小伙伴可以去看看其他大神的博文。其实我的目标是研究binder对象是如何传输的,到这里也大概也了解了,目的也算达成,后续有机会再去研究共享内存机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青山渺渺

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

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

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

打赏作者

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

抵扣说明:

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

余额充值