Android Binder 驅動 - 從驅動層來分析服務的添加過程

相關文章鏈接:

1. Android Framework - 學習啓動篇
2. Android Binder 驅動 - Media 服務的添加過程
3. Android Binder 驅動 - 啓動 ServiceManager 進程
4. Android Binder 驅動 - 內核驅動層源碼分析
5. Android Binder 驅動 - 從驅動層來分析服務的添加過程

相關源碼文件:

/drivers/android/binder.c
/drivers/staging/android/binder.c

《Android Binder 驅動 - 內核驅動層源碼分析》 一文中提到驅動層有兩個核心複雜方法 binder_thread_write 和 binder_thread_read , 由於內容過多複雜不易全篇通讀,本文我們就帶着線索去看看,先把線索挑出來:

// ServiceManager 進程:獲取判斷 binder 驅動版本號是否一致
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
  goto fail_open;
}

// ServiceManager 進程:讓 ServiceManager 進程成爲管理者
int binder_become_context_manager(struct binder_state *bs)
{
  return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

// ServiceManager 進程:binder 線程進入循環等待
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));

// ServiceManager 進程:進入循環不斷的讀寫 binder 內容
for (;;) {
  bwr.read_size = sizeof(readbuf);
  bwr.read_consumed = 0;
  bwr.read_buffer = (uintptr_t) readbuf;
  res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
  ...
}

// media 進程:添加 MediaPlayerService 服務
do {
  // 通過 ioctl 不停的讀寫操作,跟 Binder Driver 進行通信,轉發給 ServiceManager 進程
  if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
    err = NO_ERROR;
    ...
} while (err == -EINTR); //當被中斷,則繼續執行

1. 獲取 Binder 驅動版本

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
    // 從 filp 獲取 binder_proc 
	struct binder_proc *proc = filp->private_data;
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
    // 進入休眠直到中斷被喚醒
	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;
    // 從 binder_proc 獲取 binder 線程
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	switch (cmd) {
	case BINDER_VERSION: {
        // ubuf -> args -> vers
		struct binder_version __user *ver = ubuf;
		if (size != sizeof(struct binder_version)) {
			ret = -EINVAL;
			goto err;
		}
        // BINDER_CURRENT_PROTOCOL_VERSION 賦值給 vers->protocol_version
		if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
			     &ver->protocol_version)) {
			ret = -EINVAL;
			goto err;
		}
		break;
	}

	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
	return ret;
}

2. 成爲 Binder 驅動管理者

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
    // 從 filp 獲取 binder_proc 
	struct binder_proc *proc = filp->private_data;
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
    // 進入休眠直到中斷被喚醒
	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;
    // 從 binder_proc 獲取 binder 線程
	thread = binder_get_thread(proc);
	if (thread == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	switch (cmd) {
	case BINDER_SET_CONTEXT_MGR:
		ret = binder_ioctl_set_ctx_mgr(filp);
		if (ret)
			goto err;
		break;
	}

	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
	return ret;
}

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	kuid_t curr_euid = current_euid();
    // 管理者只有一個(只能被設置一次)
	if (binder_context_mgr_node != NULL) {
		pr_err("BINDER_SET_CONTEXT_MGR already set\n");
		ret = -EBUSY;
		goto out;
	}
	if (uid_valid(binder_context_mgr_uid)) {
		...
	} else {
		binder_context_mgr_uid = curr_euid;
	}
    // 靜態變量 binder_context_mgr_node = binder_new_node
	binder_context_mgr_node = binder_new_node(proc, 0, 0);
	if (binder_context_mgr_node == NULL) {
		ret = -ENOMEM;
		goto out;
	}
	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;
out:
	return ret;
}

static struct binder_node *binder_new_node(struct binder_proc *proc,
					   binder_uintptr_t ptr,
					   binder_uintptr_t cookie)
{
    // 從 proc->nodes 中獲取根節點 
	struct rb_node **p = &proc->nodes.rb_node;
	struct rb_node *parent = NULL;
	struct binder_node *node;

	while (*p) {
		parent = *p;
        // 根據 parent 的偏移量獲取 node 
		node = rb_entry(parent, struct binder_node, rb_node);

		if (ptr < node->ptr)
			p = &(*p)->rb_left;
		else if (ptr > node->ptr)
			p = &(*p)->rb_right;
		else
			return NULL;
	}
    // 剛開始肯定是 null ,創建一個 binder_node 
	node = kzalloc(sizeof(*node), GFP_KERNEL);
	if (node == NULL)
		return NULL;
	binder_stats_created(BINDER_STAT_NODE);
    // 添加到 binder_proc 的 nodes 中
	rb_link_node(&node->rb_node, parent, p);
    // 調整紅黑樹的顏色
	rb_insert_color(&node->rb_node, &proc->nodes);
	node->debug_id = ++binder_last_id;
	node->proc = proc;
	node->ptr = ptr;
	node->cookie = cookie;
	node->work.type = BINDER_WORK_NODE;
	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;
}

3. ServiceManager 進程進入循環等待

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    // 從 filp 獲取 binder_proc 
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    // 進入休眠直到中斷被喚醒
    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;
    // 從 binder_proc 獲取 binder 線程
    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

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

    default:
        ret = -EINVAL;
        goto err;
    }
    ret = 0;
    return ret;
}

static int binder_ioctl_write_read(struct file *filp,
				unsigned int cmd, unsigned long arg,
				struct binder_thread *thread)
{
	int ret = 0;
    // 從 filp 中獲取 binder_proc 
	struct binder_proc *proc = filp->private_data;
    // arg 是上層傳下來的 binder_write_read 的結構體對象地址
	void __user *ubuf = (void __user *)arg;
	struct binder_write_read bwr;

    // 將用戶空間的 binder_write_read 拷貝到內核空間的 bwr
	if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
    // 這裏是 0 ,不進去
	if (bwr.write_size > 0) {
		...
	}
    // 進入這裏不斷的讀取數據
	if (bwr.read_size > 0) {
		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);
		if (!list_empty(&proc->todo))
			wake_up_interruptible(&proc->wait);
		if (ret < 0) {
			if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
				ret = -EFAULT;
			goto out;
		}
	}
	// 將內核空間的 bwr 數據拷貝到用戶空間的 binder_write_read 
	if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
		ret = -EFAULT;
		goto out;
	}
out:
	return ret;
}

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;
    // consumed == 0
	if (*consumed == 0) {
		ptr += sizeof(uint32_t);
	}

retry:
    // 如果線程事務棧和 todo 隊列都爲空,說明此時沒有要當前線程處理的任務,將增加空閒線程的計數器(即將 wait_for_proc_work 設爲1),讓線程等待在**進程**的 wait 隊列上
	wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);

	if (wait_for_proc_work)
		proc->ready_threads++;

	binder_unlock(__func__);

	if (wait_for_proc_work) {
		if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) {
            // 線程還未進入 binder 循環,輸出錯誤信息,並阻塞直到 binder_stop_on_user_error 小於2
		    wait_event_interruptible(binder_user_error_wait,
						 binder_stop_on_user_error < 2);
		}
		binder_set_nice(proc->default_priority);
        // 非阻塞
		if (non_block) {
			...
		} else{
            // 如果是阻塞的讀操作,則讓進程阻塞在 proc 的 wait 隊列上,直到 binder_has_proc_work(thread) 爲 true,即進程有工作待處理
			ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        }
	} else {
		...
	}
    // 下面的代碼目前還執行不到,需要等待 thread 線程的 todo 裏面有內容
	return 0;
}

4. Binder 驅動添加系統服務

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    int ret = 0;
    // 從 filp 中獲取 binder_proc 
    struct binder_proc *proc = filp->private_data;
    // arg 是上層傳下來的 binder_write_read 的結構體對象地址
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;

    // 將用戶空間的 binder_write_read 拷貝到內核空間的 bwr
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    // write_size > 0 ,進入這裏
    if (bwr.write_size > 0) {
		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) {
        ...
    }
    // 將內核空間的 bwr 數據拷貝到用戶空間的 binder_write_read 
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;
}

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;
	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 == BR_OK) {
        // 獲取到 cmd 命令 BC_TRANSACTION
		if (get_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		switch (cmd) {
        ...
		case BC_TRANSACTION:
		case BC_REPLY: {
			struct binder_transaction_data tr;
            // 把數據從用戶空間拷貝到內核空間 binder_transaction_data 
			if (copy_from_user(&tr, ptr, sizeof(tr)))
				return -EFAULT;
			ptr += sizeof(tr);
			binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
			break;
		}
		...
		*consumed = ptr - buffer;
	}
	return 0;
}

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;
	binder_size_t *offp, *off_end;
    // 目標 target_proc
	struct binder_proc *target_proc;
    // 目標 target_thread 
	struct binder_thread *target_thread = NULL;
    // 目標 target_node 
	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->from_proc = proc->pid;
	e->from_thread = thread->pid;
    // handle 值
	e->target_handle = tr->target.handle;
	e->data_size = tr->data_size;
	e->offsets_size = tr->offsets_size;

	if (reply) {
		...
	} else {
		if (tr->target.handle) {
			...
		} else {
            // 添加服務時傳的 handle 值是 0 
			target_node = binder_context_mgr_node;
			if (target_node == NULL) {
				return_error = BR_DEAD_REPLY;
				goto err_no_context_mgr_node;
			}
		}
		target_proc = target_node->proc;
	}
	if (target_thread) {
		...
	} else {
		target_list = &target_proc->todo;
		target_wait = &target_proc->wait;
	}

	// binder_transaction *t;
	t = kzalloc(sizeof(*t), GFP_KERNEL);
	// binder_work *tcomplete;
	tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);

	if (!reply && !(tr->flags & TF_ONE_WAY))
		t->from = thread;
	else
		t->from = NULL;
	t->sender_euid = task_euid(proc->tsk);
	t->to_proc = target_proc;
	t->to_thread = target_thread;
    // tr->code = ADD_SERVICE_TRANSACTION
	t->code = tr->code;
	t->flags = tr->flags;
    // 往 target_proc 中按需開闢內存,物理空間和內核空間映射同一塊物理內存
	t->buffer = binder_alloc_buf(target_proc, tr->data_size,
		tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
	t->buffer->allow_user_free = 0;
	t->buffer->debug_id = t->debug_id;
	t->buffer->transaction = t;
	t->buffer->target_node = target_node;

	if (target_node)
		binder_inc_node(target_node, 1, 0, NULL);
    // 把數據拷貝到目標進程空間
	offp = (binder_size_t *)(t->buffer->data +
				 ALIGN(tr->data_size, sizeof(void *)));
	if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
			   tr->data.ptr.buffer, tr->data_size)) {
		binder_user_error("%d:%d got transaction with invalid data ptr\n",
				proc->pid, thread->pid);
		return_error = BR_FAILED_REPLY;
		goto err_copy_data_failed;
	}
	if (copy_from_user(offp, (const void __user *)(uintptr_t)
			   tr->data.ptr.offsets, tr->offsets_size)) {
		binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
				proc->pid, thread->pid);
		return_error = BR_FAILED_REPLY;
		goto err_copy_data_failed;
	}

	off_end = (void *)offp + tr->offsets_size;
	for (; offp < off_end; offp++) {
		struct flat_binder_object *fp;
        // 從目標進程中獲取 flat_binder_object 
		fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        // 判斷 type ,這裏是 BINDER_TYPE_BINDER
		switch (fp->type) {
		case BINDER_TYPE_BINDER:
		case BINDER_TYPE_WEAK_BINDER: {
			struct binder_ref *ref;
            // 從自己的進程中獲取 binder_node 節點
			struct binder_node *node = binder_get_node(proc, fp->binder);

			if (node == NULL) {
                // 根據 fp->binder 創建一個新的 binder_node 
				node = binder_new_node(proc, fp->binder, fp->cookie);
				if (node == NULL) {
					return_error = BR_FAILED_REPLY;
					goto err_binder_new_node_failed;
				}
			}
            // 根據  binder_node 節點從目標進程中獲取 binder_ref
			ref = binder_get_ref_for_node(target_proc, node);
            // 把 fp->type 替換成 BINDER_TYPE_HANDLE
			if (fp->type == BINDER_TYPE_BINDER)
				fp->type = BINDER_TYPE_HANDLE;
			else
				...
            // 給 fp->handle 賦值
			fp->handle = ref->desc;
			binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
				       &thread->todo);
			trace_binder_transaction_node_to_ref(t, node, ref);
		} break;
		...
	}

    // 往目標進程中添加一個 BINDER_WORK_TRANSACTION
	t->work.type = BINDER_WORK_TRANSACTION;
	list_add_tail(&t->work.entry, target_list);
    // 自己進程中添加一個 BINDER_WORK_TRANSACTION_COMPLETE
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	list_add_tail(&tcomplete->entry, &thread->todo);
	if (target_wait)
		wake_up_interruptible(target_wait);
	return;
}

5. ServiceManager 進程處理添加請求

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)
{
	// todo 裏面有數據了等等被喚醒
	binder_lock(__func__);

	if (wait_for_proc_work)
		proc->ready_threads--;
	thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

	if (ret)
		return ret;
    // 開始不斷循環讀取數據 
	while (1) {
		uint32_t cmd;
		struct binder_transaction_data tr;
		struct binder_work *w;
		struct binder_transaction *t = NULL;
    
		if (!list_empty(&thread->todo)) {
			...
		} else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            // 獲取 todo 隊列中的第一條
			w = list_first_entry(&proc->todo, struct binder_work,
					     entry);
		} else {
			...
		}

		switch (w->type) {
		case BINDER_WORK_TRANSACTION: {
            // 進入這個分支
			t = container_of(w, struct binder_transaction, work);
		} break;
		...
		}

		if (!t)
			continue;

		if (t->buffer->target_node) {
            // 解析參數
			struct binder_node *target_node = t->buffer->target_node;
			tr.target.ptr = target_node->ptr;
			tr.cookie =  target_node->cookie;
			cmd = BR_TRANSACTION;
		} else {
			...
		}
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
        // 解析 data 數據
		tr.data_size = t->buffer->data_size;
		tr.offsets_size = t->buffer->offsets_size;
		tr.data.ptr.buffer = (binder_uintptr_t)(
					(uintptr_t)t->buffer->data +
					proc->user_buffer_offset);
		tr.data.ptr.offsets = tr.data.ptr.buffer +
					ALIGN(t->buffer->data_size,
					    sizeof(void *));
        // 寫入命令
		if (put_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
        //  把數據拷貝到用戶空間
		if (copy_to_user(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);
        // 從 todo 隊列中移除
		list_del(&t->work.entry);
		break;
	}

done:
    ...
	return 0;
}

在這裏插入圖片描述
視頻地址:https://pan.baidu.com/s/1j_wgzITcgABVbThvO0VBPA
視頻密碼:jj4b

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