一 Binder驱动事务读取
在上一篇文章中,我们知道 Binder 驱动生成了一个 binder_transaction 事务,并把这个事务插入到了目标进程或目标线程的 todo 队列中,并唤醒了目标进程或目标线程.那么目标端是怎么处理的呢?
先说个大概流程:Binder 驱动唤醒目标进程或目标线程后,目标线程会在读取函数中休眠等待的地方醒过来,然后到自己的 todo 队列上把这个事务摘下来,然后分析这个事务,根据事务中携带的数据信息,找到目标 Binder 实体,最后把数据传递给目标Binder实体.这个只是大概的流程,接下来我们详细分析.
1.1 目标线程被唤醒
我们知道负责与 Binder 通信的都是一个个具体的线程, 当目标进程被唤醒时,实际上唤醒的是该目标进程中的某一个 Binder 线程,这个 Binder 线程正常的情况下会通过调用 binder_thread_read() 函数来读取请求者的数据,如果暂时还没有数据传输过来的时候,就会休眠阻塞在这个函数中;而如果有数据传输过来的话,则会在休眠阻塞的地方被唤醒,然后接着处理传输过来的数据,我们来看这个函数:
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;
........
//检查本线程内部的todo队列中是否有待处理的事务,如果没有,那就等待处理本进程todo队列的work
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
........
thread->looper |= BINDER_LOOPER_STATE_WAITING;//设置线程looper等待标志
........
if (non_block) {
........
} else {
//重要函数,休眠阻塞等待,被唤醒后开始往下执行
ret = binder_wait_for_work(thread, wait_for_proc_work);
}
//走到这里,证明已经被唤醒了,结束等待,需要去掉线程looper的等待状态
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
........
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
binder_inner_proc_lock(proc);
//优先处理本线程内部的 todo 队列,如果为空,则处理进程的 todo 队列
if (!binder_worklist_empty_ilocked(&thread->todo))
list = &thread->todo;//选取线程的todo
else if (!binder_worklist_empty_ilocked(&proc->todo) &&
wait_for_proc_work)
list = &proc->todo;//选取进程的todo
else {
//若无数据且当前线程looper_need_return为false, 则重试
if (ptr - buffer == 4 && !thread->looper_need_return)
goto retry;
break;
}
........
//选取好todo队列后,开始从todo队列中摘除待处理的binder_transaction事务
w = binder_dequeue_work_head_ilocked(list);//从list队列中取出第一项work
if (binder_worklist_empty_ilocked(&thread->todo))
//如果线程的todo为空,则设置process_todo false
thread->process_todo = false;
switch (w->type) {
//判断binder_work的类型,这里为BINDER_WORK_TRANSACTION
case BINDER_WORK_TRANSACTION: {
//根据work找到对应的binder_transaction事务
t = container_of(w, struct binder_transaction, work);
} break;
........
}//end switch
if (!t)
continue;
//以上我们已经拿到从请求端发送过来的binder_transaction事务
//接下来解析这个事务
if (t->buffer->target_node) {
//是否存在目标节点,这里为存在
struct binder_node *target_node = t->buffer->target_node;
struct binder_priority node_prio;
//非常重要,把Binder实体的地址赋值给tr的target.ptr
tr.target.ptr = target_node->ptr;
//非常重要,Binder实体的地址赋值给tr的target.cookie
tr.cookie = target_node->cookie;
........
cmd = BR_TRANSACTION;//构建BR_TRANSACTION命令把tr数据返回到用户空间
} else {
tr.target.ptr = 0;
tr.cookie = 0;
cmd = BR_REPLY;
}
//事务的信息全部赋值给tr这个binder_transaction_data结构,用来返回到用户空间
tr.code = t->code;
tr.flags = t->flags;
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
t_from = binder_get_txn_from(t);//获取事务t的from源线程
........
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
// 使tr中的数据缓冲区和偏移数组的地址值指向事务t中的数据缓冲区和偏移数组的地址值
tr.data.ptr.buffer = (binder_uintptr_t)
((uintptr_t)t->buffer->data +
binder_alloc_get_user_buffer_offset(&proc->alloc));
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
//将返回协议BR_TRANSACTION拷贝到目标线程提供的用户空间
if (put_user(cmd, (uint32_t __user *)ptr)) {
........
}
ptr += sizeof(uint32_t);
// 将binder_transaction_data类型的tr拷贝到目标线程提供的用户空间
if (copy_