Binder驱动之设备控制binder_ioctl -- 三

前文

3.2.2.2 数据接收及任务处理 —— binder_thread_read

简单回顾一下前面的逻辑,在binder_ioctl_write_read中,如果bwr.read_size大于0,即进程用户态希望从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;
    if (*consumed == 0) {/*当前的写入位置为bwr.read_buffer的起始位置,先写入一个BR_NOOP命令到read_buffer中,该命令在用户态是一个空操作,什么也不做,主要意义应该是在输出日志等*/
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        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 (thread->return_error != BR_OK && ptr < end) {/*之前在binder_transaction或者binder death时发生了错误*/
        if (thread->return_error2 != BR_OK) { /*发送reply时发生了错误,将错误返回给进程用户态*/
            if (put_user(thread->return_error2, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            binder_stat_br(proc, thread, thread->return_error2);
            if (ptr == end)
                goto done;
            thread->return_error2 = BR_OK;
        }
        if (put_user(thread->return_error, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        binder_stat_br(proc, thread, thread->return_error);
        thread->return_error = BR_OK;
        goto done;
    }
/*即将进入睡眠等待区,这会导致进程/线程进入阻塞状态,先将线程状态改为BINDER_LOOPER_STATE_WAITING*/
    thread->looper |= **BINDER_LOOPER_STATE_WAITING**;
    if (wait_for_proc_work)/*进程/线程没事需要处理*/
        proc->ready_threads++;/*空闲线程数+1*/
/*线程/进程将可能进入阻塞等待状态,先释放锁,这个锁是在binder_ioctl开始执行就拿了*/
    binder_unlock(__func__);
    trace_binder_wait_for_work(wait_for_proc_work,
                !!thread->transaction_stack,
                !list_empty(&thread->todo));
    if (wait_for_proc_work) {/*线程暂时没有工作要处理,进程/线程需要等待*/
        if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
                    BINDER_LOOPER_STATE_ENTERED))) {
            /* 线程还未进入binder循环,输出错误信息,并阻塞直到binder_stop_on_user_error小于2*/
            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_set_nice(proc->default_priority);
        if (non_block) {/*设置了非阻塞标识*/
            if (!binder_has_proc_work(proc, thread)) /*检查当前进程是否有工作待处理,如果没有就将返回值设为-EAGAIN,以便用户进程稍后重试*/
                ret = -**EAGAIN**;
        } else/*如果是阻塞的读操作,则让进程阻塞在proc的wait队列上,直到binder_has_proc_work(thread)为true,即进程有工作待处理*/
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {/**/
        if (non_block) {/*读操作设置了非阻塞标识*/
            if (!binder_has_thread_work(thread)) /*检查当前线程是否有工作待处理,如果没有就将返回值设为-EAGAIN,以便用户进程稍后重试*/
                ret = -**EAGAIN**;
        } else/*如果是阻塞的读操作,则让线程阻塞在thread的wait队列上,直到binder_has_thread_work(thread)为true,即线程有工作待处理*/
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
    }
    /*运行到这里,要么是线程/进程没有工作待处理,但是讲返回值ret设置成了-EAGAIN;要么是线程/进程已经有工作待处理了*/
    binder_lock(__func__); /*重新获取锁*/
    if (wait_for_proc_work)/*之前进入了等待操作,线程被挂起了*/
        proc->ready_threads--;/*空闲线程数减1*/
    thread->looper &= **~BINDER_LOOPER_STATE_WAITING**;/*移除线程等待标志位*/
    /* We cannot return -ERESTARTSYS here.  This code is called
    * after binder_thread_write() has already interpreted the
    * input buffer.  A restart will result in a doubled set of
    * commands.  Just return success, having consumed zero
    * bytes. */
    if (ret)
        return ret == -ERESTARTSYS ? 0 : ret;
/*开始循环处理thread/proc的todo队列上的每一个binder_work*/
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
    /*取出一个binder work来处理*/
        if (!list_empty(&thread->todo)) {/*线程的待处理列表不为空*/
            w = list_first_entry(&thread->todo, struct binder_work, /*从线程的待处理列表队头中取出一项工作处理*/
                        entry);
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {/*进程的待处理列表不为空,且睡眠等待前线程的`todo`队列和事务栈都为空*/
            w = list_first_entry(&proc->todo, struct binder_work, /*从进程的待处理列表的队头中取出一项工作处理*/
                        entry);
        } else {
            /* no data added */
            if (ptr - buffer == 4 &&
                !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                goto retry;
            break;
        }
        if (end - ptr < sizeof(tr) + 4)
            break;
        switch (w->type) {
        case **BINDER_WORK_TRANSACTION**: {/*要处理的是一个事务(Binder请求)*/
            /*根据binder_work在binder_transaction的偏移计算出binder_transaction的地址*/
            t = container_of(w, struct binder_transaction, work);
        } break;
        case **BINDER_WORK_TRANSACTION_COMPLETE**: {
            /*TRANSACTION或者REPLY发送完成消息,通过给进程发送BR_TRANSACTION_COMPLETE告知*/
            cmd = BR_TRANSACTION_COMPLETE;
            if (put_user(cmd, (uint32_t __user *)ptr))/*发送给用户进程*/
                return -EFAULT;
            ptr += sizeof(uint32_t);
            binder_stat_br(proc, thread, cmd);/*更新统计数据*/
            binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
                    "%d:%d BR_TRANSACTION_COMPLETE\n",
                    proc->pid, thread->pid);
            list_del(&w->entry);/*从todo队列中移除*/
            kfree(w);           /*释放在binder_thread_write在处理BC_TRANSACTION命令时在binder_transaction中申请的binder_work*/
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); /*更新BINDER_STAT_TRANSACTION_COMPLETE统计数据*/
        } break;
        case **BINDER_WORK_NODE**: {/*取出的binder_work是一个binder_node*/
            struct binder_node *node = container_of(w, struct binder_node, work); /*根据偏移计算出binder_node的地址*/
            uint32_t cmd = BR_NOOP;
            const char *cmd_name;
            int strong = node->internal_strong_refs || node->local_strong_refs;
            int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;
            if (weak && !node->has_weak_ref) {/*弱引用计数不为0,但是弱引用标志位为0*/
                cmd = BR_INCREFS;    /*发送BR_INCREFS命令给进程用户态,让其增加弱引用计数*/
                cmd_name = "BR_INCREFS";
                node->has_weak_ref = 1; /*设置弱引用标志位*/
                node->pending_weak_ref = 1;  /*设置pengding标志位,表示(进程用户态)有未处理的弱引用增加命令*/
                node->local_weak_refs++; /*增加本地弱引用计数器*/
            } else if (strong && !node->has_strong_ref) {/*强引用计数不为0,但是强引用标志位为0*/
                cmd = BR_ACQUIRE; /*发送BR_ACQUIRE命令给进程,让其增加强引用计数*/
                cmd_name = "BR_ACQUIRE";
                node->has_strong_ref = 1;  /*设置强引用标志位*/
                node->pending_strong_ref = 1; /*设置pengding标志位,表示(进程用户态)有未处理的强引用增加命令*/
                node->local_strong_refs++; /*增加本地强引用计数器*/
            } else if (!strong && node->has_strong_ref) {/*强引用计数为0,但是强引用标志位不为0*/
                cmd = BR_RELEASE;
                cmd_name = "BR_RELEASE";
                node->has_strong_ref = 0;
            } else if (!weak && node->has_weak_ref) {/*弱引用计数为0,但是弱引用标志位不为0*/
                cmd = BR_DECREFS;
                cmd_name = "BR_DECREFS";
                node->has_weak_ref = 0;
            }
            if (cmd != BR_NOOP) {/*有引用计数相关的命令需要处理*/
                /*将命令先发送给进程用户态*/
                if (put_user(cmd, (uint32_t __user *)ptr))
                    return -EFAULT;
                ptr += sizeof(uint32_t);
                /*BBinder的引用计数器的地址发送给进程的用户态地址空间read_buffer*/
                if (put_user(node->ptr,
                        (binder_uintptr_t __user *)ptr))
                    return -EFAULT;
                ptr += sizeof(binder_uintptr_t);
                /*BBinder的地址发送给进程*/
                if (put_user(node->cookie,
                        (binder_uintptr_t __user *)ptr))
                    return -EFAULT;
                ptr += sizeof(binder_uintptr_t);
                /*更新统计数据*/
                binder_stat_br(proc, thread, cmd);
                binder_debug(BINDER_DEBUG_USER_REFS,
                        "%d:%d %s %d u%016llx c%016llx\n",
                        proc->pid, thread->pid, cmd_name,
                        node->debug_id,
                        (u64)node->ptr, (u64)node->cookie);
            } else {/*不需要增加/减少binder_node的强/弱引用计数*/
                list_del_init(&w->entry);/*从todo队列中移出*/
                if (!weak && !strong) {/*binder_node的强弱引用计数都为0,释放该binder_node*/
                    binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                            "%d:%d node %d u%016llx c%016llx deleted\n",
                            proc->pid, thread->pid,
                            node->debug_id,
                            (u64)node->ptr,
                            (u64)node->cookie);
                    rb_erase(&node->rb_node, &proc->nodes);/*从proc->nodes红黑树中移除*/
                    kfree(node);/*释放binder_node所占内存空间*/
                    binder_stats_deleted(BINDER_STAT_NODE);/*更新统计数据*/
                } else {
                    binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                            "%d:%d node %d u%016llx c%016llx state unchanged\n",
                            proc->pid, thread->pid,
                            node->debug_id,
                            (u64)node->ptr,
                            (u64)node->cookie);
                }
            }
        } break;
        /*binder service死亡相关的几个命令处理*/
        case **BINDER_WORK_DEAD_BINDER**:
        case **BINDER_WORK_DEAD_BINDER_AND_CLEAR**:
        case **BINDER_WORK_CLEAR_DEATH_NOTIFICATION**: {
            struct binder_ref_death *death;
            uint32_t cmd;
            death = container_of(w, struct binder_ref_death, work);/*根据偏移计算出包含它的binder_ref_death对象的地址*/
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)/*死亡通知清理完毕的消息*/
                cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;/*回复命令设为BR_CLEAR_DEATH_NOTIFICATION,告知用户进程清除通知完毕的相关处理已完成*/
            else
                cmd = BR_DEAD_BINDER;/*告诉用户进程,binder service已经死亡*/
            if (put_user(cmd, (uint32_t __user *)ptr))  /*将命令发送给用户*/
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (put_user(death->cookie,
                    (binder_uintptr_t __user *)ptr))/*客户端对象(BpBinder)对应的地址发送到用户进程*/
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);
            binder_stat_br(proc, thread, cmd);
            binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
                    "%d:%d %s %016llx\n",
                    proc->pid, thread->pid,
                    cmd == BR_DEAD_BINDER ?
                    "BR_DEAD_BINDER" :
                    "BR_CLEAR_DEATH_NOTIFICATION_DONE",
                    (u64)death->cookie);
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
                list_del(&w->entry);
                kfree(death);
                binder_stats_deleted(BINDER_STAT_DEATH);
            } else
            /*`BINDER_WORK_DEAD_BINDER`和`BINDER_WORK_DEAD_BINDER_AND_CLEAR`移到proc->delivered_deat队列*/
                list_move(&w->entry, &proc->delivered_death);
            if (cmd == BR_DEAD_BINDER)
                goto done; /* DEAD_BINDER notifications can cause transactions */
        } break;
        }//end of switch
    /*当binder_work的类型是BINDER_WORK_TRANSACTION时,t不为NULL*/
        if (!t)
            continue;
    /*接下来开始处理**TRANSACTION**,将binder_transaction转换为进程用户态使用的binder_transaction_data*/
        BUG_ON(t->buffer == NULL);
        /* 在binder_transaction章节中我们知道,当binder客户端向binder服务端发送请求时,
        * target_node为binder服务端的binder_node地址,如果是binder服务端回复客户端,则target_node为NULL。
        */
        if (t->buffer->target_node) {/*Client->Server的binder请求*/
            struct binder_node *target_node = t->buffer->target_node;
    /*将引用计数器地址及BBinder地址写入transaction data中, 即将struct binder_transaction转化为进程用户态可处理struct binder_transaction_data结构体*/
            tr.target.ptr = target_node->ptr;
            tr.cookie =  target_node->cookie;
            /*设置线程优先级信息*/
            t->saved_priority = task_nice(current);
            if (t->priority < target_node->min_priority &&
                !(t->flags & TF_ONE_WAY))
                binder_set_nice(t->priority);
            else if (!(t->flags & TF_ONE_WAY) ||
                t->saved_priority > target_node->min_priority)
                binder_set_nice(target_node->min_priority);
            cmd = BR_TRANSACTION;
        } else {/*Client->Server的binder请求的回复*/
            /* 将引用计数器地址及BBinder地址从transaction data清空,
            * 因为Client无法从地址中获取相应的对象,这个地址只有在服务端的进程的地址空间才有效。
            */
            tr.target.ptr = 0;
            tr.cookie = 0;
            cmd = BR_REPLY;
        }
        tr.code = t->code;/*设置transacton的业务代码,一种代码对应一种binder server提供的服务*/
        tr.flags = t->flags;/*设置transacton的标识位*/
        tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);/*请求线程的有eudi*/
    /*设置发送端进程id*/
        if (t->from) {
            struct task_struct *sender = t->from->proc->tsk;
            tr.sender_pid = task_tgid_nr_ns(sender,
                            task_active_pid_ns(current));
        } else {
            tr.sender_pid = 0;
        }
    /*数据相关信息*/
        tr.data_size = t->buffer->data_size;     /*数据大小*/
        tr.offsets_size = t->buffer->offsets_size; /*offsets区大小*/
        /*将binder_transaction_data中数据指针直接转换成binder_buffer映射到用户态地址,**这样就无需再从内核态拷贝到用户态的操作了***/
        tr.**data.ptr.buffer** = (binder_uintptr_t)( 
                    (uintptr_t)t->buffer->data +
                    proc->user_buffer_offset);
        /*将binder_transaction_data的offset区地址设置为binder_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**))/*将BINDER_TRANACTION或者BINDER_REPLEY命令发送到进程用户态*/
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))/*将binder_transaction_data拷贝到进程用户态*/
            return -EFAULT;
        ptr += sizeof(tr);
        trace_binder_transaction_received(t);
        binder_stat_br(proc, thread, cmd);/*更新统计数据*/
        binder_debug(BINDER_DEBUG_TRANSACTION,
                "%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
                proc->pid, thread->pid,
                (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
                "BR_REPLY",
                t->debug_id, t->from ? t->from->proc->pid : 0,
                t->from ? t->from->pid : 0, cmd,
                t->buffer->data_size, t->buffer->offsets_size,
                (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
        list_del(&t->work.entry);/*从todo队列中移除*/
        t->buffer->allow_user_free = 1; /*因为这块地址已经交给进程用户态使用了,因此运行进程释放该段地址空间*/
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {/*BINDER_TRANSACTION*/
            /*加入事务栈*/
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
        } else {/*BINDER_REPLY*/
            /*一次binder通信已完成,释放binder_transaction*/
            t->buffer->transaction = NULL;
            kfree(t);
            binder_stats_deleted(BINDER_STAT_TRANSACTION);/*更新被删除相关的统计数据*/
        }
        **break;/*处理完一个BINDER_WORK_TRANSACTION,就退出循环。说明对于BINDER_WORK_TRANSACTION每次至多只处理一个*/**
    }//end while
done:
    *consumed = ptr - buffer;/*发送给用户态的字节数*/
    if (proc->requested_threads + proc->ready_threads == 0 &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
        BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
        /*spawn a new thread if we leave this out */) {
        /* 没有可用的binder线程,且之前已经进入到BINDER_LOOPER_STATE_REGISTERED
        * 或者BINDER_LOOPER_STATE_ENTERED状态启动一个新的binder线程 */
        proc->requested_threads++;
        binder_debug(BINDER_DEBUG_THREADS,
                "%d:%d BR_SPAWN_LOOPER\n",
                proc->pid, thread->pid);
        if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
            return -EFAULT;
        binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
    }
    return 0;
}

binder_thread_read的代码量相较于binder_thread_write会容易一些,主体处理逻辑如下:

  • 首先根据*consumed,即binder_write_read结构体的read_consumed域,是否为0判断当前的可写入指针是不是在bwr.read_buffer的起始位置,是的话就先写入一个BR_NOOP命令。由此可见,在bwr.read_buffer中,它总是以一个BR_NOOP命令开头的
  • 接着检查之前的处理是否有错误发生(return_errorreturn_error2),是的话将错误码写入用户态地址空间的read_buffer中,然后跳过中间的处理逻辑,直接到达最后判断是否需要创建线程操作;
  • 如果之前没有错误发生,就接着看当前线程任务栈及todo队列是否还有任务未处理,以决定是等待在线程还是在进程的wait队列上。如果没有任务要处理 —— todo队列为空,且用户态不需要内核态返回值,则根据进程是否设置非阻塞标志位,设置了就返回-EAGAIN,表示稍后重试;未设置且没有任务要处理就等待在进程/线程的wait队列上,等到有任务要处理时再被wake_up。这里有三个小点需要注意:
    • 线程进入睡眠前要先释放之前拿到的binder锁,这个锁是一个大锁,binder驱动的全局数据都靠其保护,如果线程睡眠前不释放锁,其他binder线程很可能都要阻塞在等待这个锁上。当线程再次等被唤醒后,会重新获取锁。
    • 线程在进入睡眠前和唤醒后要分别设置和取消BINDER_LOOPER_STATE_WAITING状态标志位。
    • 如果线程是等待进程的wait队列上,睡眠前和唤醒后要分别增减空闲线程数—— proc->ready_threads
  • 开始循环处理线程/进程的todo队列上的任务。处理进程todo队列的条件是线程的todo队列已经处理完了,进程的todo队列不为空,且之前等待在进程的wait队列上。对于每一个binder_work的处理流程,则根据其类型,走各自的处理逻辑:
    • BINDER_WORK_TRANSACTION, 根据binder_workbinder_transaction结构体的偏移计算出binder_transaction对象的地址。然后根据是Client->Server的binder请求还是Server->Client的回复(t->buffer->target_node是否为NULL),确定发送给用户态进程的cmdBR_TRANSACTION还是BR_REPLY。如果是Client->Server的请求,还要根据target_node,得到binder server对象在server所在进程的用户态地址(cookie)及其相关的引用计数(ptr),填入binder_transaction_data中。再就是将binder请求的相关信息,如发送进程的pid,有效用户id,code, flags及数据相关信息。这里需要单独提出来讲一下的是transaction相关data区及offsets区的内容,并不需要从内核态拷贝到用户态的操作,只需将binder_transaction_datadata.ptr.bufferdata.ptr.offsets两个指针修改为相应用户态地址即可。可以这样做的原因是binder_bufferdata所指的缓冲区的物理页框同时映射到了用户态和内核态的虚拟机地址空间,即binder_buffer.data(内核态)和binder_buffer.data + user_buffer_offset(用户态)两段虚拟地址空间映射的是同一组物理页框。这里就是Binder进程间通信高效的精髓所在,只需要一次发送端的用户态到内核态拷贝即可,接收端只需简单修改指针就好了。这部分内容的不太清楚的可以参考一下之前写的Binder驱动之binder_buffer的分配与回收第二节内容。剩下的工作就是将cmdbinder_transaction_data发送到用户态(binder_write_read.read_buffer),从todo移除该binder_work,加入事务栈(BR_TRANSACTION)或者释放binder_transaction(BR_REPLY)等。还有一个要说明的是对于BINDER_WORK_TRANSACTIONbinder_work,一次binder_thread_read操作只会执行一个,处理完了就跳出循环,以便线程可以回到用户态处理本次Binder Transaction。
  • BINDER_WORK_TRANSACTION_COMPLETED,这个的主要用途是用来告知进程binder请求或者回复已经发出去,整体逻辑比较简单,代码中已添加相关注释,就不再赘述了。
  • BINDER_WORK_NODE,这是一个处理binder_nodebinder service(BBinder)强弱引用计数相关的命令。当binder_node有强/弱引用时,确保其对应服务端进程用户态地址空间中binder service对象,不会被释放;当binder_node有强/弱引用归0时,递减其对应服务端进程用户态地址空间中binder service对象引用计数,以确保用户地址空间的对象会被正确释放。
  • BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION,这三种binder_work是binder service死亡相关几个work。从前文的binder_thread_write中的死亡通知的几个命令中,已经基本讲清楚了这几个类型及队列的转移过程,可以回头重新看一下。

3.3 注册Context Manager —— BINDER_SET_CONTEXT_MGR

注册Context Manager是Android系统进程SM(ServiceManager)启动时执行的操作,它是整个Android系统中binder service的大管家,所有想通过binder提供服务的service都要在SM中注册(匿名的binder service通过已注册的service间接查找),才能被使用者找到。
该命令在驱动是通过binder_ioctl_set_ctx_mgr函数完成的,函数实现如下:

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();/*获取当前进程的有效用户id*/
    if (binder_context_mgr_node != NULL) {
        pr_err("BINDER_SET_CONTEXT_MGR already set\n");
        ret = -EBUSY;
        goto out;
    }
    ret = security_binder_set_context_mgr(proc->tsk);/*检查当前进程是否有权限执行注册Context Manager的权限,在4.4版本的内核中该函数直接返回0.`kernel/include/linux/security.h`*/
    if (ret < 0)
        goto out;
    if (uid_valid(binder_context_mgr_uid)) {/*检查已有的uid是否有效*/
        if (!uid_eq(binder_context_mgr_uid, curr_euid)) {/*有效但是与当前运行线程的有效用户ID不相等,则出错。即线程只能注册自己,且只能有一个线程设置为Context Manager*/
            pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
                from_kuid(&init_user_ns, curr_euid),
                from_kuid(&init_user_ns,
                    binder_context_mgr_uid));
            ret = -EPERM;
            goto out;
        }
    } else {/*之前设置的无效(如初始化状态,它的值是INVALID_UID),设置成当前进程的有效用户id。Service Manager启动第一次注册时走这个分支*/
        binder_context_mgr_uid = curr_euid;
    }
    binder_context_mgr_node = binder_new_node(proc, 0, 0);/*分配一个全新的binder_node,并赋值到全局变量binder_context_mgr_node中*/
    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;
}
  • 可以看到主体逻辑其实非常简单,先做了一下权限检查,再判断是否已经注册过,接着如果没有注册过就将当前进程的有效用户id设置到全局变量binder_context_mgr_uid中,并分配一个新的binder_node,更新相关引用计数就大功告成了。

3.4 线程退出命令 —— BINDER_THREAD_EXIT

该命令是在线程析构时(IPCThreadState::threadDestructor)发出的。Android线程在初始化时(IPCThreadState::self())会调用pthread_key_create注册TLS(变量名为:gTLS)销毁时的函数,即IPCThreadState::threadDestructor,它在线程退出时会自动被系统调用。驱动收到该命令后调用binder_free_thread来处理,函数实现如下:

static int binder_free_thread(struct binder_proc *proc,
                struct binder_thread *thread)
{
    struct binder_transaction *t;
    struct binder_transaction *send_reply = NULL;
    int active_transactions = 0;
    rb_erase(&thread->rb_node, &proc->threads);/*在进程的线程红黑树中删除该线程节点*/
    t = thread->transaction_stack;
    if (t && t->to_thread == thread) /*事务栈中还有需要本线程处理的事务,记录到send_reply中,稍后回复失败消息给对方*/
        send_reply = t;
    /* 依次处理事务栈的两类事务: 
    * a)需要本线程处理的事务,to_thread == thread.
    * b) 等待其他线程回复的事务,from_thread == thread.
    */
    while (t) {
        active_transactions++;
        binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
                "release %d:%d transaction %d %s, still active\n",
                proc->pid, thread->pid,
                t->debug_id,
                (t->to_thread == thread) ? "in" : "out");
        if (t->to_thread == thread) {
            t->to_proc = NULL;
            t->to_thread = NULL;
            if (t->buffer) {
                t->buffer->transaction = NULL;
                t->buffer = NULL;
            }
            t = t->to_parent;
        } else if (t->from == thread) {
            t->from = NULL;
            t = t->from_parent;
        } else
            BUG();
    }
    if (send_reply)/*事务栈中有待本线程处理的事务,给对端线程发送BR_DEAD_REPLY消息*/
        binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
    binder_release_work(&thread->todo);/*将todo队列上面所有待处理binder_work全部释放*/
    kfree(thread); /*释放binder_thread本身所占用的结构体*/
    binder_stats_deleted(BINDER_STAT_THREAD); /*更新统计数据*/
    return active_transactions; /*返回待处理的事务数*/
}

对于线程退出命令BINDER_THREAD_EXIT的处理主体逻辑可分为四个步骤:
a)在进程的线程红黑树(proc->threads)中移除该线程节点。
b)删除事务栈没有处理完的事务,并向对端发送一个BR_READ_REPLY消息。
c)释放线程的待处理列表(todo)上的所有binder_work。
d)释放thread结构体,更新相关统计数据。

3.5 获取Binder版本信息 ----- BINDER_VERSION

这部分内容比较简单,相关逻辑都已在binder_ioctrl函数switchBINDER_VERSION分支中做了注释,这里就不再赘述了。

总结

  • Binder驱动中的命令可分为两类,一类是以BC_xxx的形式,如BC_TRANSACTIONBC_REPLY,它们是进程用户态发送Binder驱动的处理的命令;一类是以BR_xxx的形式,它们是Binder驱动回复给进程用户态处理的命令,如BR_REPLYBR_TRANSACTIONBR_OK等。Binder驱动中的处理逻辑基本都是围绕这些命令展开的。这些命令总结起来大体可以分为以下几类:

    • binder_nodebinder_ref引用计数处理相关。因为每一个BBinderBpBinder都要一个binder_nodebinder_ref与其相对应,需要正确处理其引用计数才能保证不会造成内存泄漏发生。
      • BR_INCREFS, BR_ACQUIRE, BR_RELEASE, BR_DECREFS
      • BC_INCREFS, BC_ACQUIRE, BC_RELEASE, BC_DECREFS, BC_ACQUIRE_DONE, BC_INCREF_DONEBC_ATTEMPT_ACQUIRE(这个命令还不支持)。
    • Binder请求和回复相关的命令。
      • BR_ERROR,BR_OK,BR_TRANSACTION, BR_TRANSACTION_COMPLETE, BR_REPLY, BR_DEAD_REPLY, BR_FAILED_REPLY, BR_NOOP
      • BC_TRANSACTION, BC_REPLY,
    • Binder线程/进程状态,包括注册成ServiceManager,创建线程。
      • BR_SPAWN_LOOPER
      • BC_REGISTER_LOOPER, BC_ENTER_LOOPER, BC_EXIT_LOOPER
    • Binder死亡即"讣告"(通知)相关的命令。
      • BR_DEAD_BINDER, BR_CLEAR_DEATH_NOTIFICATION_DONE
      • BC_REQUEST_DEATH_NOTIFICATION, BC_CLEAR_DEATH_NOTIFICATION, BC_DEAD_BINDER_DONE
    • Binder缓冲区释放命令
      • BC_FREE_BUFFER
  • struct binder_transaction_datastruct binder_transaction分别Binder通信中数据的用户态和驱动层的表示,当数据从用户态用binder_transaction_data发送到内核驱动后,驱动会将其转化成一个binder_transaction,然后加上自己处理需要的一些数据结构,如work等。其中数据部分(data域)转换成binder_buffer; 如果是client发送给binder server的请求,还会根据target, cookie的值找到binder service对应的binder_node,存放在binder_buffertarget_node中。如果是binder server回复给client数据,则binder_buffer.target_nodeNULL

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容