开篇
本篇以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
提供的支持接着做分析