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怎麼接收和處理事務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章