Binder native层源码分析(四):Binder驱动将数据发往sm

在源码分析(三),数据最终由ioctl调用交由驱动处理。android源码不包括驱动代码,驱动代码需要另外下载。网上有很多下载驱动代码的方法。如果嫌麻烦可以直接在github随便找一个手机品牌的驱动代码下载。

其实驱动层是独立的一层,不应该归于native层,但为了让对Binder底层实现的整个分析过程更为连续,这里就不换标题了。

在分析驱动层的数据传输过程之前,有必要了解一下Binder驱动的打开和映射操作。这两个操作为Binder通信做了数据结构和内存空间上的准备,所以至少要知道里面一些重要的数据结构和为Binder通信分配的内核缓冲区的概念。

Binder驱动的注册与打开(可跳过)

Binder被android系统注册成misc device类型的驱动,这种类型的驱动主设备号统一为10,次设备号为每种设备独有,且只需调用misc_register()就可完成注册:

//\drivers\android\binder.c
static int __init init_binder_device(const char *name)
{
	int ret;
	struct binder_device *binder_device;
//...
	binder_device->miscdev.fops = &binder_fops;//文件操作
	binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
	binder_device->miscdev.name = name;
//...
	ret = misc_register(&binder_device->miscdev);//注册
/...
}

fops即file operations,指该驱动支持的文件操作,如下:

static const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.compat_ioctl = binder_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};

其中最常用的就是ioctl,mmap和open。

binder_open用于打开驱动,进程在访问Binder驱动时,首先得先打开/dev/binder节点:

static int binder_open(struct inode *nodp, struct file *filp)
{
	struct binder_proc *proc;
	struct binder_device *binder_dev;

	proc = kzalloc(sizeof(*proc), GFP_KERNEL);
	if (proc == NULL)
		return -ENOMEM;
	spin_lock_init(&proc->inner_lock);
	spin_lock_init(&proc->outer_lock);
	get_task_struct(current->group_leader);
	proc->tsk = current->group_leader;
	mutex_init(&proc->files_lock);
	INIT_LIST_HEAD(&proc->todo);//todo链表,里面存放待处理的Binder通信工作项
	if (binder_supported_policy(current->policy)) {
		proc->default_priority.sched_policy = current->policy;
		proc->default_priority.prio = current->normal_prio;
	} else {
		proc->default_priority.sched_policy = SCHED_NORMAL;
		proc->default_priority.prio = NICE_TO_PRIO(0);
	}

	binder_dev = container_of(filp->private_data, struct binder_device,
				  miscdev);
	proc->context = &binder_dev->context;
	binder_alloc_init(&proc->alloc);

	binder_stats_created(BINDER_STAT_PROC);
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	INIT_LIST_HEAD(&proc->waiting_threads);//等待链表
	filp->private_data = proc;//放到filp中

	mutex_lock(&binder_procs_lock);//上锁
	hlist_add_head(&proc->proc_node, &binder_procs);//放到binder_proc队列头部
	mutex_unlock(&binder_procs_lock);
//...后面是debug代码,略去
	return 0;
}

上面代码主要完成的工作就是创建并初始化binder_proc proc,它代表了一个正在使用Binder的进程,存储了Binder分配给该进程的内核空间等重要信息。

binder_mmap

mmap即Memory Map,可以将某个设备或者文件映射到进程的内存空间中,方便直接对文件/设备进行修改。

binder_mmap是在内核空间为本进程分配缓冲区,同时在用户空间中映射该缓冲区。之后,其它进程就可以在内核态可以访问该缓冲区,进行数据的拷贝。

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
//vm_area_struct vma描述一块应用程序使用的虚拟内存(应用空间)
	int ret;
	struct binder_proc *proc = filp->private_data;//取得binder_proc实体
	const char *failure_string;

	if (proc->tsk != current->group_leader)
		return -EINVAL;

	if ((vma->vm_end - vma->vm_start) > SZ_4M)
		vma->vm_end = vma->vm_start + SZ_4M;//最大分配4M
//...
	ret = binder_alloc_mmap_handler(&proc->alloc, vma);
	if (ret)
		return ret;
	mutex_lock(&proc->files_lock);
	proc->files = get_files_struct(current);
	mutex_unlock(&proc->files_lock);
	return 0;
//...
}

vma中记录的一块用户虚拟空间,它被传递给了binder_alloc_mmap_handler,该函数的作用是分配一块内核虚拟空间,该内核虚拟空间和用户虚拟空间对应同一块物理空间。

//\drivers\android\binder_alloc.c
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
			      struct vm_area_struct *vma)
{
	int ret;
	struct vm_struct *area;//内核中对虚拟内存的描述(内核空间)
	const char *failure_string;
	struct binder_buffer *buffer;

	mutex_lock(&binder_alloc_mmap_lock);
	if (alloc->buffer) {//如果已经映射
		ret = -EBUSY;
		failure_string = "already mapped";
		goto err_already_mapped;
	}
//为Binder获取可用的虚拟空间(此时还未分配物理空间??)
//注意vma是应用程序的虚拟空间,area则是内核虚拟空间
	area = get_vm_area(vma->vm_end - vma->vm_start, VM_ALLOC);
	if (area == NULL) {
		ret = -ENOMEM;
		failure_string = "get_vm_area";
		goto err_get_vm_area_failed;
	}
	alloc->buffer = area->addr;//buffer指针指向该内核虚拟空间
	alloc->user_buffer_offset =
		vma->vm_start - (uintptr_t)alloc->buffer;
	mutex_unlock(&binder_alloc_mmap_lock);
//...
//pages指向物理页面
	alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
				   ((vma->vm_end - vma->vm_start) / PAGE_SIZE),
			       GFP_KERNEL);
	if (alloc->pages == NULL) {
		ret = -ENOMEM;
		failure_string = "alloc page array";
		goto err_alloc_pages_failed;
	}
	alloc->buffer_size = vma->vm_end - vma->vm_start;

	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
	if (!buffer) {
		ret = -ENOMEM;
		failure_string = "alloc buffer struct";
		goto err_alloc_buf_struct_failed;
	}

	buffer->data = alloc->buffer;//buffer指向一整块内核缓冲区
	list_add(&buffer->entry, &alloc->buffers);//将内存块放到buffers链表中
	buffer->free = 1;//此内存可用
	binder_insert_free_buffer(alloc, buffer);//将该buffer插入到alloc的空闲红黑树中。
	alloc->free_async_space = alloc->buffer_size / 2;
	binder_alloc_set_vma(alloc, vma);
	mmgrab(alloc->vma_vm_mm);

	return 0;

//...
}

上面的代码后半部分有好几个buffer(s),要读懂这部分代码干了什么首先得理清代码中几个buffer(s)的含义。先看binder_alloc结构体中的buffer:

binder_alloc与linux中特殊的链表写法

struct binder_alloc {
	struct mutex mutex;
	struct vm_area_struct *vma;//用户地址空间
	struct mm_struct *vma_vm_mm;
	void *buffer;//内核缓冲区的地址
	ptrdiff_t user_buffer_offset;
	//将buffer指向的地址块分成若干小块,每一小块用binder_buffer结构体记录,
	struct list_head buffers;
	//free_buffers和allocated_buffers是空闲小块和已分配小块的红黑树。
	struct rb_root free_buffers;
	struct rb_root allocated_buffers;
	size_t free_async_space;
	struct binder_lru_page *pages;
	size_t buffer_size;
	uint32_t buffer_free;//空闲缓冲区大小
	int pid;
	size_t pages_high;
};

需要注意的是,尽管buffers的类型为list_head,free_buffers和allocated_buffers的类型为rb_root,但它们都可以理解为以binder_buffer为元素的数据结构。这是linux代码中一种特殊的链表写法。我们看完binder_buffer的定义就清楚是怎么一回事了:

struct binder_buffer {
	struct list_head entry; /* free and allocated entries by address */
	struct rb_node rb_node; /* free entry by size or allocated entry */
				/* by address */
	unsigned free:1;
	unsigned allow_user_free:1;
	unsigned async_transaction:1;
	unsigned debug_id:29;

	struct binder_transaction *transaction;//正在使用该buffer的事务

	struct binder_node *target_node;//正在使用该buffer的Binder实体对象
	size_t data_size;//该buffer的大小
	size_t offsets_size;
	size_t extra_buffers_size;
	void *data;
};

可以看到,entry的类型是list_head,和buffers的类型一样。实际上,buffers中的元素是binder_buffer.entry,但由于entry是binder_buffer的成员变量,它们的地址偏移是固定的,一旦得到了entry的地址,也就可以算出binder_buffer的地址。所以为了便于理解,我们逻辑上可以把buffers当成以binder_buffer为元素的链表。free_buffers和allocated_buffers也同理,可以把它们当作元素为binder_buffer的红黑树。

再回到binder_alloc_mmap_handler(),其实该函数就是在内核分配了一块缓冲空间,令alloc->buffer指向该空间的地址,再创建了一个binder_buffer结构体,然后令这个结构体描述这整块内核空间(初始情况下没有事务使用缓冲,因此整块空间都是空闲的),并且插入到alloc的buffers和free_buffers中去。

binder_mmap总结

总结一下binder_mmap干的事情,binder_mmap分别在内核虚拟空间中开辟了一块空间,并且该内核空间和传递进来的用户空间对应一块物理空间,如图:
在这里插入图片描述
binder_proc->alloc->buffer中放的是内核地址,mmap返回的是用户空间地址。两者经过地址转换后指向同一个物理地址。

由于内核空间共享,如果在另一进程的内核态中取得了本进程的binder_proc,就可以得到本进程的binder_mmap分配的地址空间,这就实现了空间的共享,然后就可以向该空间复制数据,只需一次复制就可以实现数据的传递。如下图(图来自深入理解android内核设计思想)
在这里插入图片描述

binder_ioctl

在talkWithDriver中以如下方式调用了ioctl

ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

我们看ioctl具体是做了什么(分段阅读):

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
	struct binder_proc *proc = filp->private_data;//取出binder_proc
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;//即bwr
//...
	trace_binder_ioctl(cmd, arg);
//挂起进程,等待唤醒
	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;
		
	thread = binder_get_thread(proc);//从proc中找到(新建)一个线程
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

其中wait_event_interruptible函数是进程调度中挂起进程的实现方法。

在wait_event_interruptible中,会判断condition是否满足,若不满足,则进入死循环,在循环中首先将自己设为可中断挂起状态,然后进行schedule()调度(我以前一直搞不清楚进程切换的代码是否属于该进程,其实代码就在内核空间中,而内核空间被所有进程共享)。当该进程被唤醒后,会再进行condition判断,若依旧不满足,则继续中断挂起。

后面就开始处理命令了,这里只看talkWithDriver中使用的命令BINDER_WRITE_READ

	switch (cmd) {
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;

交给binder_ioctl_write_read处理。

binder_ioctl_write_read

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

	if (size != sizeof(struct binder_write_read)) {
		ret = -EINVAL;
		goto out;
	}
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}

copy_from_user表示从用户空间中复制bwr。talkWithDriver构建的bwr位于用户空间,而ioctl位于内核空间,所以首先得先从将bwr复制到内核空间。

函数剩余部分如下:

	if (bwr.write_size > 0) {
//binder_thread_write完成写
		ret = binder_thread_write(proc, thread,
					  bwr.write_buffer,
					  bwr.write_size,
					  &bwr.write_consumed);
		trace_binder_write_done(ret);
		if (ret < 0) {//发生错误
			bwr.read_consumed = 0;
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
	if (bwr.read_size > 0) {
//binder_thread_read完成读
		ret = binder_thread_read(proc, thread, bwr.read_buffer,
					 bwr.read_size,
					 &bwr.read_consumed,
					 filp->f_flags & O_NONBLOCK);
		trace_binder_read_done(ret);
		binder_inner_proc_lock(proc);
		if (!binder_worklist_empty_ilocked(&proc->todo))
			binder_wakeup_proc_ilocked(proc);
		binder_inner_proc_unlock(proc);
		if (ret < 0) {
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
//...
}

根据bwr的读写大小判断是否需要读写。读写由binder_thread_write/read完成。注意这里它将bwr.*_consumed的地址传进了函数。在源码分析三中,我们知道bwr记录的是mIn和mOut的信息,而mOut中放在我们要传输的数据,故write_size必不为0,所以会进入binder_thread_write函数。

binder_thread_write

我们只看该函数是怎么处理BC_TRANSACTION协议的。

/*binder_thread_write(proc, thread,
					  bwr.write_buffer,//mOut.data
					  bwr.write_size,
					  &bwr.write_consumed);*/
static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,
			binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{
	uint32_t cmd;
	struct binder_context *context = proc->context;
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	while (ptr < end && thread->return_error.cmd == BR_OK) {
		int ret;

		if (get_user(cmd, (uint32_t __user *)ptr))//读出协议,即BC_TRANSACTION
			return -EFAULT;
		ptr += sizeof(uint32_t);//更新读写指针
		trace_binder_command(cmd);
		if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
			atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
			atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
			atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
		}
		switch (cmd) {
		case BC_TRANSACTION:
		case BC_REPLY: {
			struct binder_transaction_data tr;

			if (copy_from_user(&tr, ptr, sizeof(tr)))
				return -EFAULT;
			ptr += sizeof(tr);
			binder_transaction(proc, thread, &tr,
					   cmd == BC_REPLY, 0);
//...
			

Binder数据的构成如下,这里即根据bwr的指针信息从mOut中读出了tr,然后交给了binder_transaction()处理。
在这里插入图片描述

binder_transaction分段1

继续看binder_transaction函数,这个函数非常长,处理了BC_REPLY和BC_TRANSACTION。在源码分析(二)中我们知道,传输的协议为BC_TRANSACTION,handle值为0,据此截取部分代码如下(分段阅读)。

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)
{
//...		
	if (reply) {//...}//协议为BC_REPLY的分支
	else {//协议为BC_TRANSACTION的分支,进入该分支
		if (tr->target.handle) {//...}//当handle值不为0的分支
		else {//当handle==0,即目标进程是service_manage时进入这个分支
			mutex_lock(&context->context_mgr_node_lock);
			target_node = context->binder_context_mgr_node;//直接从全局变量中获取sm进程的binder_node
			if (target_node)
			//从binder_node中得到目标进程的binder_proc
				target_node = binder_get_node_refs_for_txn(
						target_node, &target_proc,
						&return_error);
			else
				return_error = BR_DEAD_REPLY;
			mutex_unlock(&context->context_mgr_node_lock);
//...
		}

上面的代码从全局环境中取得service manager的binder_node和binder_proc。继续往下看:

	t = kzalloc(sizeof(*t), GFP_KERNEL);//生成一个binder_transaction对象
	tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);//生成biner_work对象
//...
	if (!reply && !(tr->flags & TF_ONE_WAY))
		t->from = thread;//from等于本线程
	else
		t->from = NULL;
	t->sender_euid = task_euid(proc->tsk);
	t->to_proc = target_proc;//sm的binder_proc
	t->to_thread = target_thread;//to等于目标线程,为NULL
	t->code = tr->code;//记录code(请求码)
	t->flags = tr->flags;
//...
//从目标进程的通信空间中分配一块空间用于本次通信。关于binder_buffer类型见mmap
	t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
		tr->offsets_size, extra_buffers_size,
		!reply && (t->flags & TF_ONE_WAY));
//...
	t->buffer->debug_id = t->debug_id;
	t->buffer->transaction = t;
	t->buffer->target_node = target_node;//目标结点。
	trace_binder_transaction_alloc_buf(t->buffer);

这里创建了一个binder_transaction对象指针t,从类型名来看这个对象应该就是传输的事务(消息)。然后创建了binder_work对象指针tcomplete,用于通知t传输完毕。

从后面的代码来看,binder_transaction里也有一个binder_work类型的成员,这个binder_work的指针会被放在目标线程的todo链表,而tcomplete会被放在本线程的todo链表。也就是说,binder_work代表一个Binder工作内容,线程会从链表从取出该变量并根据其类型来完成工作。

t->work的类型为BINDER_WORK_TRANSACTION,binder_thread_read在遇到这种类型时会根据binder_work在binder_transaction中的偏移得到bnder_transaction的地址,即得到t,这样就完成了事务的传输。

后面的代码就是对填写事务t,注意其中的buffer就是前面mmap中的那块buffer。继续往下看:

	if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
			   tr->data.ptr.buffer, tr->data_size)) {
//...
	}
	//复制object,由Parcel数据结构知offset中存储的是offset。
	if (copy_from_user(offp, (const void __user *)(uintptr_t)
			   tr->data.ptr.offsets, tr->offsets_size)) {
//...
	}

申请完内存后,将数据和object复制。由于buffer所指向的内存空间是共享的,因此只需要一次复制,如图(图来自深入理解android内核设计思想):
在这里插入图片描述
在上图中,binder_proc指的都是进程A的binder_proc,由于内核空间共享,所以在进程B的内核态可以取得进程A的binder_binder_proc,也就可以直接向进程A的空间复制数据。

之后一个很长的for循环是对object进行处理,在源码分析(二)对Parcel的分析中我们知道,传输的object的类型是BINDER_TYPE_BINDER,我们看驱动是怎么处理该类型的object的:

	for (; offp < off_end; offp++) {
		struct binder_object_header *hdr;
		size_t object_size = binder_validate_object(t->buffer, *offp);
//...
/*	struct binder_object_header {
	__u32        type;
};*/ //其实就是指flat_binder_object的第一个成员type
		hdr = (struct binder_object_header *)(t->buffer->data + *offp);
		off_min = *offp + 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);//从数据中恢复flat_binder_object
			ret = binder_translate_binder(fp, t, thread);
			if (ret < 0) {
				return_error = BR_FAILED_REPLY;
				return_error_param = ret;
				return_error_line = __LINE__;
				goto err_translate_failed;
			}
		} break;
		//...

取得flat_binder_object后,传给了binder_translate_binder处理。

binder_translate_binder分段1:创建(找到)obj对应binder_node

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;//即sm的binder_proc
	struct binder_ref_data rdata;
	int ret = 0;
//从sm的binder_proc的红黑树中找binder_node
//由于MediaPlayerService是新建的,所以在binder_proc中找不到该binder_node
	node = binder_get_node(proc, fp->binder);
	if (!node) {
//创建一个新binder_node,并插入到本进程的binder_proc的红黑树中
		node = binder_new_node(proc, fp);
		if (!node)
			return -ENOMEM;
	}
	if (fp->cookie != node->cookie) {
		binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
				  proc->pid, thread->pid, (u64)fp->binder,
				  node->debug_id, (u64)fp->cookie,
				  (u64)node->cookie);
		ret = -EINVAL;
		goto done;
	}
	if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
		ret = -EPERM;
		goto done;
	}

根据传递的flat_binder_obj创建了一个新的binder_node,我们看是怎么创建的。

binder_get_node和binder_new_node

binder_get_node中调用了binder_get_node_ilocked

//ptr是flat_binder_obj->binder,即MediaPlayerService的weakref_type
//proc是指本进程的binder_proc,也就是服务进程的binder_proc。
static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc,
						   binder_uintptr_t ptr)
{
	struct rb_node *n = proc->nodes.rb_node;
	struct binder_node *node;

	assert_spin_locked(&proc->inner_lock);

	while (n) {
		node = rb_entry(n, struct binder_node, rb_node);
		//binder_proc是以服务类的弱引用指针作为键值来建树的
		if (ptr < node->ptr)
			n = n->rb_left;
		else if (ptr > node->ptr)
			n = n->rb_right;
		else {
			/*
			 * take an implicit weak reference
			 * to ensure node stays alive until
			 * call to binder_put_node()
			 */
			binder_inc_node_tmpref_ilocked(node);
			return node;
		}
	}
	return NULL;
}

在源码分析二中分析writeStrongBinder时我们知道,flat_binder_obj的binder成员存储的是MediaPlayerService的弱引用指针。上面的代码就是将该弱引用指针作为键值来在红黑树中进行查找。由于是第一次查找,所以肯定找不到,返回NULL。

binder_new_node中创建了一个binder_node类型交给binder_init_node_ilocked进行初始化。

static struct binder_node *binder_init_node_ilocked(
						struct binder_proc *proc,
						struct binder_node *new_node,
						struct flat_binder_object *fp)
{
	struct rb_node **p = &proc->nodes.rb_node;
	struct rb_node *parent = NULL;
	struct binder_node *node;
	//binder即MediaPlayerService的弱引用类型
	binder_uintptr_t ptr = fp ? fp->binder : 0;
	//cookie即MediaPlayerService的指针
	binder_uintptr_t cookie = fp ? fp->cookie : 0;
	__u32 flags = fp ? fp->flags : 0;
	s8 priority;

	assert_spin_locked(&proc->inner_lock);
//从proc的红黑树中查找是否以及存在该binder_node,若存在则直接返回
//前面已经看过查找的代码了,所以跳过这部分
	while (*p) {
//...
	}
//proc的红黑树并不存在该node,此时对new_node进行初始化。
	node = new_node;
	binder_stats_created(BINDER_STAT_NODE);
	node->tmp_refs++;
	//插入到proc的红黑树中
	rb_link_node(&node->rb_node, parent, p);
	rb_insert_color(&node->rb_node, &proc->nodes);
	node->debug_id = atomic_inc_return(&binder_last_id);
	node->proc = proc;//等于本binder_proc
	node->ptr = ptr;//弱引用指针
	node->cookie = cookie;//服务类
	node->work.type = BINDER_WORK_NODE;
	priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
	node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >>
		FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
	node->min_priority = to_kernel_prio(node->sched_policy, priority);
	node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
	node->inherit_rt = !!(flags & FLAT_BINDER_FLAG_INHERIT_RT);
	spin_lock_init(&node->lock);
	INIT_LIST_HEAD(&node->work.entry);
	INIT_LIST_HEAD(&node->async_todo);
	binder_debug(BINDER_DEBUG_INTERNAL_REFS,
		     "%d:%d node %d u%016llx c%016llx created\n",
		     proc->pid, current->pid, node->debug_id,
		     (u64)node->ptr, (u64)node->cookie);

	return node;
}

对new_node进行了初始化,其中将flat_binder_obj的binder和cookie(即服务类的弱引用指针和服务类指针)赋给了binder_node的ptr和cookie,该node会被插入到本进程(服务进程)的binder_proc

binder_translate_binder分段2:修改obj

创建完binder_node之后并插入到本进程的binder_proc中后,我们看 binder_translate_binder还干了什么

//rdata的地址被传进了该函数,该函数会对rdata进行赋值
//注意传入的是sm的binder_proc!!!
	ret = binder_inc_ref_for_node(target_proc, node,
			fp->hdr.type == BINDER_TYPE_BINDER,
			&thread->todo, &rdata);
	if (ret)
		goto done;

	if (fp->hdr.type == BINDER_TYPE_BINDER)
		fp->hdr.type = BINDER_TYPE_HANDLE;//注意这里,type被换成了HANDLE
	else
		fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
	fp->binder = 0;
	fp->handle = rdata.desc;
	fp->cookie = 0;

	trace_binder_transaction_node_to_ref(t, node, &rdata);
	binder_debug(BINDER_DEBUG_TRANSACTION,
		     "        node %d u%016llx -> ref %d desc %d\n",
		     node->debug_id, (u64)node->ptr,
		     rdata.debug_id, rdata.desc);
done:
	binder_put_node(node);
	return ret;
}

总结一下,当传递的是BINDER_TYPE_BINDER类型的object时,驱动会将该object的类型换成BINDER_TYPE_HANDLE,并令其handle成员值等于rdata.desc。

而rdata在函数binder_inc_ref_for_node中被赋值,我们看其究竟是什么。

binder_inc_ref_for_node:增加binder_node引用计数

注意传入的是sm的binder_proc。

/*binder_inc_ref_for_node(target_proc, node,
			fp->hdr.type == BINDER_TYPE_BINDER,
			&thread->todo, &rdata);*/
static int binder_inc_ref_for_node(struct binder_proc *proc,
			struct binder_node *node,
			bool strong,
			struct list_head *target_list,
			struct binder_ref_data *rdata)
{
	struct binder_ref *ref;
	struct binder_ref *new_ref = NULL;
	int ret = 0;

	binder_proc_lock(proc);
	//根据binder_node在sm的binder_proc中找binder_ref
	//由于MediaPlayerService时新建的,所以sm的binder_proc中找不到该binder_ref
	ref = binder_get_ref_for_node_olocked(proc, node, NULL);
	if (!ref) {//若找不到,则新建一个binder_ref并插入binder_proc
		binder_proc_unlock(proc);
		new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
		if (!new_ref)
			return -ENOMEM;
		binder_proc_lock(proc);
		//将创建的binder_ref插入到sm的binder_proc中并返回。
		ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
	}
	//见下
	ret = binder_inc_ref_olocked(ref, strong, target_list);
	*rdata = ref->data;//rdata等于binder_ref的data
	binder_proc_unlock(proc);
	if (new_ref && ref != new_ref)
		/*
		 * Another thread created the ref first so
		 * free the one we allocated
		 */
		kfree(new_ref);
	return ret;
}

rdata其实就是binder_ref的data成员变量,而binder_ref在binder_get_ref_for_node_olocked中被初始化。在创建完binder_ref后,会增加binder_ref的引用值。我们先看binder_ref的初始化过程。

binder_get_ref_for_node_olocked为binder_node创建引用

static struct binder_ref *binder_get_ref_for_node_olocked(
					struct binder_proc *proc,
					struct binder_node *node,
					struct binder_ref *new_ref)
{
	struct binder_context *context = proc->context;
	struct rb_node **p = &proc->refs_by_node.rb_node;
	struct rb_node *parent = NULL;
	struct binder_ref *ref;
	struct rb_node *n;
//在binder_proc中找binder_ref
	while (*p) {
		parent = *p;
		ref = rb_entry(parent, struct binder_ref, rb_node_node);

		if (node < ref->node)
			p = &(*p)->rb_left;
		else if (node > ref->node)
			p = &(*p)->rb_right;
		else
			return ref;
	}
	if (!new_ref)
		return NULL;
//若找不到,则初始化new_ref并插入binder_proc的refs_by_node红黑树中
	binder_stats_created(BINDER_STAT_REF);
	new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
	//赋值node和proc
	new_ref->proc = proc;
	new_ref->node = node;
	//将新的binder_ref插入到binder_proc的refs_by_node红黑树中
	rb_link_node(&new_ref->rb_node_node, parent, p);
	rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
//从这里可以看出service manager的desc(handle)为0
	new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
//我们传递的MediaPlayerService非service manager,所以这里找一个空闲的值作为
//其desc(handle),而前面代码中赋给flat_binder_object的handle值就是该desc。
	for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
		ref = rb_entry(n, struct binder_ref, rb_node_desc);
		if (ref->data.desc > new_ref->data.desc)
			break;
		new_ref->data.desc = ref->data.desc + 1;
	}
//从binder_proc的refs_by_desc红黑树中找到一个合适的位置将新binder_ref插入
	p = &proc->refs_by_desc.rb_node;
	while (*p) {
		parent = *p;
		ref = rb_entry(parent, struct binder_ref, rb_node_desc);

		if (new_ref->data.desc < ref->data.desc)
			p = &(*p)->rb_left;
		else if (new_ref->data.desc > ref->data.desc)
			p = &(*p)->rb_right;
		else
			BUG();
	}
	rb_link_node(&new_ref->rb_node_desc, parent, p);
	rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);

	binder_node_lock(node);
//插入到binder_node的红黑树中
	hlist_add_head(&new_ref->node_entry, &node->refs);

	binder_debug(BINDER_DEBUG_INTERNAL_REFS,
		     "%d new ref %d desc %d for node %d\n",
		      proc->pid, new_ref->data.debug_id, new_ref->data.desc,
		      node->debug_id);
	binder_node_unlock(node);
	return new_ref;
}

也就是说,驱动为binder_node创建了一个binder_ref引用,并插入到service_manager的binder_proc的两颗红黑树中,并且赋予该binder_ref一个非重复的空闲值作为desc(binder_ref.data.desc)。该值作为binder_ref在binder_proc红黑树中的键值。前面赋给flat_binder_obj的handle值就是指该desc。

以后,就可以通过该键值,在sm的binder_proc中找到相应的binder_ref,进而找到相应的binder_node。

binder_inc_ref_olocked增加引用

static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
				  struct list_head *target_list)
{
	int ret;

	if (strong) {//进入该分支
		if (ref->data.strong == 0) {//进入该分支
		//相当于node->internal_strong_refs++
			ret = binder_inc_node(ref->node, 1, 1, target_list);
			if (ret)
				return ret;
		}
		ref->data.strong++;//ref中data的强引用也加一
	} else {
		if (ref->data.weak == 0) {
			ret = binder_inc_node(ref->node, 0, 1, target_list);
			if (ret)
				return ret;
		}
		ref->data.weak++;
	}
	return 0;
}

同时增加了binder_node和binder_ref的引用成员。

binder_proc,binder_node,binder_ref与handle总结

前面分析代码的过程中大量涉及到这三个数据结构,这里对它们做一个介绍。

首先是binder_proc,在Binder驱动的注册与打开中我们知道每个打开binder驱动的进程都会有一个binder_proc结构体。其中binder_proc的几颗红黑树描述了客户端和服务端之间的关系。

struct binder_proc {
	struct hlist_node proc_node;//该binder_proc在全局hash表中的结点。
	struct rb_root threads;//处理Binder通信的线程池。
	struct rb_root nodes;//以binder_node为结点的红黑树
	struct rb_root refs_by_desc;//以binder_ref的desc值为键值的红黑树
	struct rb_root refs_by_node;//以binder_ref的node为键值的红黑树。
	struct list_head waiting_threads;
	int pid;
	struct task_struct *tsk;// 指向进程任务控制块
	struct files_struct *files;// 指向进程打开文件结构体数组
	struct mutex files_lock;
	struct hlist_node deferred_work_node;
	int deferred_work;
	bool is_dead;

	struct list_head todo; // 工作项,每个工作项代表一个通信请求
	struct binder_stats stats;
	struct list_head delivered_death;
	int max_threads;
	int requested_threads;
	int requested_threads_started;
	int tmp_ref;
	struct binder_priority default_priority;
	struct dentry *debugfs_entry;
	struct binder_alloc alloc;//用来管理内核缓冲
	struct binder_context *context;
	spinlock_t inner_lock;
	spinlock_t outer_lock;
};

binder_node描述一个Binder实体对象,每一个Service组件在驱动层都对应一个binder_node。

struct binder_node {
	int debug_id;
	spinlock_t lock;
	struct binder_work work;
	union {
		struct rb_node rb_node;	//该binder_node在binder_proc的红黑树中的结点
		struct hlist_node dead_node;
	};
	struct binder_proc *proc;//指向宿主进程的binder_proc
	struct hlist_head refs;//元素为binder_ref的红黑树。
	//引用计数
	int internal_strong_refs;
	int local_weak_refs;
	int local_strong_refs;
	int tmp_refs;
	binder_uintptr_t ptr;// 描述用户空间中的Service组件,指向Service的影子对象???
	binder_uintptr_t cookie;// 描述用户空间中的Service组件的地址,指向Service的地址
	struct {
		/*
		 * bitfield elements protected by
		 * proc inner_lock
		 */
		u8 has_strong_ref:1;
		u8 pending_strong_ref:1;
		u8 has_weak_ref:1;
		u8 pending_weak_ref:1;
	};
	struct {
		/*
		 * invariant after initialization
		 */
		u8 sched_policy:2;
		u8 inherit_rt:1;
		u8 accept_fds:1;
		u8 min_priority;
	};
	bool has_async_transaction;
	struct list_head async_todo;
};

一个Binder实体对象可能会同时被多个Client组件引用,Binder驱动使用结构体binder_ref来描述这些引用关系。

struct binder_ref {
	/* Lookups needed: */
	/*   node + proc => ref (transaction) */
	/*   desc + proc => ref (transaction, inc/dec ref) */
	/*   node => refs + procs (proc exit) */
	struct binder_ref_data data;//记录了desc和引用数等信息
	//以下两个成员是binder_proc中的红黑树结点
	struct rb_node rb_node_desc;//以desc为关键字构造的红黑树结点
	struct rb_node rb_node_node;//以binder_node为关键字构造的红黑树结点。
	struct hlist_node node_entry;
	struct binder_proc *proc;
	struct binder_node *node;//所引用的binder_node
	struct binder_ref_death *death;
};

从前面的代码分析可知它们的关系如下:
在这里插入图片描述
注意binder_ref和binder_node是以红黑树结点的形式放于binder_proc中,上图是为了便于理解才这样画的。而handle值的作用在于从binder_proc的红黑树中找到该binder_ref,进而得到binder_node。

binder_transaction分段2

弄清楚binder_proc,binder_node和binder_ref的关系后,我们继续看binder_transaction的代码

//t->work和tcomplete一样也是binder_transaction,记住下面它们的类型,待会分析binder_thread_read时会用到。
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
t->work.type = BINDER_WORK_TRANSACTION;
 if (reply) {...}
 else if (!(t->flags & TF_ONE_WAY)) {
		BUG_ON(t->buffer->async_transaction != 0);
		binder_inner_proc_lock(proc);
//把tcomplete加入本线程的todo队列中
		binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
		t->need_reply = 1;
		t->from_parent = thread->transaction_stack;
		thread->transaction_stack = t;//本线程的transaction_stack等于t
		binder_inner_proc_unlock(proc);
//binder_proc_transaction把t(t->work)放到对方线程的todo队列中。
		if (!binder_proc_transaction(t, target_proc, target_thread)) {
			binder_inner_proc_lock(proc);
			binder_pop_transaction_ilocked(thread, t);
			binder_inner_proc_unlock(proc);
			goto err_dead_proc_or_thread;
		}
	} else {...}
//...

最后就是设置t->work和tcomplete的类型,并将其加入对方线程和自己线程的todo队列。

到这里binder_thread_write完成了对BC_TRANSACTION协议的处理,已经将事务(消息)放到了sm的todo队列中。下一篇博文将介绍service_manager怎么接收和处理事务。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值