开篇
本篇以aosp分支android-11.0.0_r25,kernel分支android-msm-wahoo-4.4-android11作为基础解析
上一篇文章Android源码分析 - Binder驱动(中),我们分析了binder_ioctl中的写操作binder_thread_write部分,了解了binder请求的发起与调度,接下来我们就进行binder驱动的最后一部分分析,binder_thread_read
binder_ioctl_write_read
我们还是先从binder_ioctl后的BINDER_WRITE_READ命令码开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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 (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; } ... 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); 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; } } ... out: return ret; }
|
binder_thread_read
这是进行binder读操作的函数,这个函数也是比较长,我们同样将它分成几个部分:
- 等待可用的
binder_work
- 循环获取
todo队列中的binder_work,并根据binder_work的type,执行一定的处理
- 处理
binder_transaction以及binder_transaction_data,并将binder_transaction_data拷贝回用户空间
第一部分:等待工作
binder_thread.looper
在此之前我们需要先看一下之前提到的,在binder_thread中的域成员looper,前面我们只是注释了这个域表示线程状态,这里我们介绍一下它有哪些取值:
BINDER_LOOPER_STATE_REGISTERED:表示该binder线程是非主binder线程
BINDER_LOOPER_STATE_ENTERED:表示该binder线程是主binder线程
BINDER_LOOPER_STATE_EXITED:表示该binder线程马上就要退出了
BINDER_LOOPER_STATE_INVALID:表示该binder线程是无效的(比如原来是binder主线程,后续用户又发送了一个BC_REGISTER_LOOPER请求)
BINDER_LOOPER_STATE_WAITING:表示当前binder线程正在等待请求
BINDER_LOOPER_STATE_NEED_RETURN:表示该binder线程在处理完transaction后需要返回到用户态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| 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;
if (*consumed == 0) { if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); }
retry: binder_inner_proc_lock(proc); wait_for_proc_work = binder_available_for_proc_work_ilocked(thread); binder_inner_proc_unlock(proc);
thread->looper |= BINDER_LOOPER_STATE_WAITING;
if (wait_for_proc_work) { if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n", proc->pid, thread->pid, thread->looper); wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); } binder_restore_priority(current, proc->default_priority); }
if (non_block) { if (!binder_has_work(thread, wait_for_proc_work)) ret = -EAGAIN; } else { ret = binder_wait_for_work(thread, wait_for_proc_work); }
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
if (ret) return ret; ... }
|
这一部分先检查是否有可用的binder_work待处理,如果有的话进入到下一部分,如果没有的话则需要等待
具体的,我们先看这个函数中的wait_for_proc_work变量,这个变量表示是否需要等待binder_proc中的工作,当在binder_thread中找不到事务栈并且todo队列为空时,此变量值为true
1 2 3 4 5 6 7
| static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread) { return !thread->transaction_stack && binder_worklist_empty_ilocked(&thread->todo) && (thread->looper & (BINDER_LOOPER_STATE_ENTERED | BINDER_LOOPER_STATE_REGISTERED)); }
|
在上一篇文章Android源码分析 - Binder驱动(中),我们分析过是有分支会将binder_work直接添加到binder_proc的todo队列中的,当binder_thread中找不到工作的话,可能就需要从binder_proc中找了
然后会将当前线程的状态置为等待中,等到有可处理的binder_work后再解除这个状态
之后会判断传入的参数是否为阻塞模式,这个是由framework层执行ioctl时传入的flags所决定的,根据framework中的binder代码,这里应该恒为阻塞模式,在阻塞模式下,会调用binder_wait_for_work函数,等待存在可用的binder_work
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| static int binder_wait_for_work(struct binder_thread *thread, bool do_proc_work) { DEFINE_WAIT(wait); struct binder_proc *proc = thread->proc; int ret = 0;
freezer_do_not_count(); binder_inner_proc_lock(proc); for (;;) { prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE); if (binder_has_work_ilocked(thread, do_proc_work)) break; if (do_proc_work) list_add(&thread->waiting_thread_node, &proc->waiting_threads); binder_inner_proc_unlock(proc); schedule(); binder_inner_proc_lock(proc); list_del_init(&thread->waiting_thread_node); if (signal_pending(current)) { ret = -ERESTARTSYS; break; } } finish_wait(&thread->wait, &wait); binder_inner_proc_unlock(proc); freezer_count();
return ret; }
static bool binder_has_work_ilocked(struct binder_thread *thread, bool do_proc_work) { return thread->process_todo || thread->looper_need_return || (do_proc_work && !binder_worklist_empty_ilocked(&thread->proc->todo)); }
|
这个函数的结构和Linux函数wait_event_interruptible非常相似,我们先来看看它是怎么做到阻塞等待的
Linux进程调度
DEFINE_WAIT
这是一个宏,它定义了一个wait_queue_t结构体
1 2 3 4 5 6 7 8
| #define DEFINE_WAIT_FUNC(name, function) \ wait_queue_t name = { \ .private = current, \ .func = function, \ .task_list = LIST_HEAD_INIT((name).task_list), \ }
#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
|
这个结构体中的private域指向的即是当前执行系统调用所在进程的描述结构体,func域指向唤醒这个队列项进程所执行的函数
prepare_to_wait
这个函数将我们刚刚定义的wait队列项加入到一个等待队列中(在binder中即是加入到thread->wait中),然后将进程的状态设置为我们指定的状态,在这里为TASK_INTERRUPTIBLE(可中断的睡眠状态)
schedule
schedule是真正执行进程调度的地方,由于之前进程状态已经被设置成TASK_INTERRUPTIBLE状态,在调用schedule后,该进程就会让出CPU,不再调度运行,直到该进程恢复TASK_RUNNING状态
wake_up_interruptible
我们在上一篇文章Android源码分析 - Binder驱动(中)中提到过,当binder_transaction工作处理完后,会调用wake_up_interruptible函数唤醒目标binder线程的等待队列
这个函数会唤醒TASK_INTERRUPTIBLE状态下的进程,它会循环遍历等待队列中的每个元素,分别执行其唤醒函数,也就对应着我们DEFINE_WAIT定义出来的结构体中的func域,即autoremove_wake_function,它最终会调用try_to_wake_up函数将进程置为TASK_RUNNING状态,这样后面的进程调度便会调度到该进程,从而唤醒该进程继续执行
signal_pending
这个函数是用来检查当前系统调用进程是否有信号需要处理的,当一个进程陷入系统调用并处于等待状态时,如果此时产生了一个信号,仅仅是在该进程的thread_info中标识了一下,所以我们唤醒进程后需要检查一下是否有信号需要处理,如果有的话,返回-ERESTARTSYS,先处理信号,后续Linux上层库函数会根据-ERESTARTSYS此返回值重新执行这个系统调用
finish_wait
最后一步,当进程被唤醒后,调用finish_wait函数执行清理工作,将当前进程置为TASK_RUNNING状态,并把当前wait队列项从等待队列中移除
了解了上面这些知识,我们再看binder_wait_for_work函数应该比较清晰了,这里有一点需要注意,我们在上一篇文章Android源码分析 - Binder驱动(中)提到,在没有TF_ONE_WAY标志的情况下,会使用binder_enqueue_deferred_thread_work_ilocked函数将tcomplete插入到事务发起binder线程的todo队列中,这个函数区别于binder_enqueue_thread_work_ilocked函数,它没有将thread->process_todo设为true,所以结合着binder_has_work_ilocked函数看,我们可以发现,当thread->process_todo为false时,整个binder_has_work_ilocked返回false,即会进入到睡眠状态,延迟执行BINDER_WORK_TRANSACTION_COMPLETE,这么设计可以让binder接收端优先处理事务,提高了性能
第二部分:获取工作,根据type做一定的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| 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; ... retry: ... while (1) { uint32_t cmd; struct binder_transaction_data_secctx tr; struct binder_transaction_data *trd = &tr.transaction_data; struct binder_work *w = NULL; struct list_head *list = NULL; struct binder_transaction *t = NULL; struct binder_thread *t_from; size_t trsize = sizeof(*trd);
binder_inner_proc_lock(proc); if (!binder_worklist_empty_ilocked(&thread->todo)) list = &thread->todo; else if (!binder_worklist_empty_ilocked(&proc->todo) && wait_for_proc_work) list = &proc->todo; else { binder_inner_proc_unlock(proc);
if (ptr - buffer == 4 && !thread->looper_need_return) goto retry; break; }
if (end - ptr < sizeof(tr) + 4) { binder_inner_proc_unlock(proc); break; } w = binder_dequeue_work_head_ilocked(list); if (binder_worklist_empty_ilocked(&thread->todo)) thread->process_todo = false;
switch (w->type) { case BINDER_WORK_TRANSACTION: { binder_inner_proc_unlock(proc); t = container_of(w, struct binder_transaction, work); } break; case BINDER_WORK_RETURN_ERROR: { ... } break; case BINDER_WORK_TRANSACTION_COMPLETE: { binder_inner_proc_unlock(proc); cmd = BR_TRANSACTION_COMPLETE; if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, cmd); kfree(w); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); } break; case BINDER_WORK_NODE: { ... } break; case BINDER_WORK_DEAD_BINDER: case BINDER_WORK_DEAD_BINDER_AND_CLEAR: case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { ... } break; } ... } done: ... }
|
这里先创建了一个binder_transaction_data_secctx结构体,后续会将它拷贝到用户空间去,然后创建了一个指针trd指向tr.transaction_data的地址,这样后续操作trd就相当于操作tr.transaction_data了
当进程从睡眠中唤醒,意味着有可用的binder_work了,这时候理论上来说,binder_thread和binder_proc其中总有一个todo队列不为空,这里优先处理binder_thread的todo队列,如果两者都为空,且还未读取过任何数据,重新goto到retry处等待
接着就是将binder_work从相应的todo队列中出队,再根据其类型执行不同的处理操作,这里我们只针对BINDER_WORK_TRANSACTION和BINDER_WORK_TRANSACTION_COMPLETE这两种最重要的类型分析
当类型为BINDER_WORK_TRANSACTION时,表示是别的进程向自己发起binder请求,此时,我们根据binder_work找到对应的binder_transaction结构
当类型为BINDER_WORK_TRANSACTION_COMPLETE时,表示发起的请求BC_TRANSACTION已经完成了,此时将回复给用户空间BR_TRANSACTION_COMPLETE响应码,然后更新统计数据,释放资源
第三部分:处理binder_transaction,拷贝回用户空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
| 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; ... retry: ... while (1) { uint32_t cmd; struct binder_transaction_data_secctx tr; struct binder_transaction_data *trd = &tr.transaction_data; struct binder_work *w = NULL; struct list_head *list = NULL; struct binder_transaction *t = NULL; struct binder_thread *t_from; size_t trsize = sizeof(*trd); ... if (!t) continue;
if (t->buffer->target_node) { struct binder_node *target_node = t->buffer->target_node; struct binder_priority node_prio;
trd->target.ptr = target_node->ptr; trd->cookie = target_node->cookie; node_prio.sched_policy = target_node->sched_policy; node_prio.prio = target_node->min_priority; binder_transaction_priority(current, t, node_prio, target_node->inherit_rt); cmd = BR_TRANSACTION; } else { trd->target.ptr = 0; trd->cookie = 0; cmd = BR_REPLY; } trd->code = t->code; trd->flags = t->flags; trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);
t_from = binder_get_txn_from(t); if (t_from) { struct task_struct *sender = t_from->proc->tsk; trd->sender_pid = task_tgid_nr_ns(sender, task_active_pid_ns(current)); } else { trd->sender_pid = 0; }
trd->data_size = t->buffer->data_size; trd->offsets_size = t->buffer->offsets_size; trd->data.ptr.buffer = (binder_uintptr_t) ((uintptr_t)t->buffer->data + binder_alloc_get_user_buffer_offset(&proc->alloc)); trd->data.ptr.offsets = trd->data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
tr.secctx = t->security_ctx; if (t->security_ctx) { cmd = BR_TRANSACTION_SEC_CTX; trsize = sizeof(tr); } if (put_user(cmd, (uint32_t __user *)ptr)) { ... } ptr += sizeof(uint32_t); if (copy_to_user(ptr, &tr, trsize)) { ... } ptr += trsize; binder_stat_br(proc, thread, cmd);
if (t_from) binder_thread_dec_tmpref(t_from); t->buffer->allow_user_free = 1; if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) { binder_inner_proc_lock(thread->proc); t->to_parent = thread->transaction_stack; t->to_thread = thread; thread->transaction_stack = t; binder_inner_proc_unlock(thread->proc); } else { binder_free_transaction(t); } break; }
done: *consumed = ptr - buffer; binder_inner_proc_lock(proc); if (proc->requested_threads == 0 && list_empty(&thread->proc->waiting_threads) && proc->requested_threads_started < proc->max_threads && (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) ) { proc->requested_threads++; binder_inner_proc_unlock(proc); if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) return -EFAULT; binder_stat_br(proc, thread, BR_SPAWN_LOOPER); } else binder_inner_proc_unlock(proc); return 0; }
|
只有当type == BINDER_WORK_TRANSACTION的情况下,才会走后面的这些逻辑,这一部分主要做的工作是,将处理事务所需要的信息(命令码、PID等)和数据(数据区首地址和偏移数组地址)准备好,拷贝到用户空间,交给用户空间处理这个事务,还有一些细节都在上面代码注释中标出来了,应该还是比较清晰的
结束
到这里,从client向binder驱动发起请求,到binder驱动找到server并将请求传递给server这一整个流程我们基本上是看完了,对于被TF_ONE_WAY标记的事务(无需返回),在server中处理完,这一整条binder进程通信流程也就结束了,而对于需要返回的事务,则还需要server向binder驱动发起BC_REPLY事务请求,进行一次进程间通信,将处理后的返回值传给client进程
总结
我们已经将binder驱动所承担的工作分析的七七八八了,但肯定还有很多地方大家云里雾里(实际上我也是这样的),这是因为binder进程间通信不仅需要binder驱动的处理,还需要framework层的协助,很多事情,比如说binder对象的压扁打包就是在framework层做的,我们在binder驱动层只关注了传输,但却不知道传输的是什么,结构是怎样的,当然会产生各种各样的疑惑,后面我们就将会对framework层对binder提供的支持接着做分析