05. Android Binder图解 小米权威系统专家 解析binder总结调用流程 (安卓12)

需要掌握的点

1.一个流程:servier--->binder---->servermanager ** server注册的流程图一个整体的类和方法流向**

2.重要的几个方法:open ,mmap ,iocontro。特别是mmap内存映射 (图片解析)

3.唤醒流程图,bp和br

4.binder_thread_write 和 binder_thread_read 和 copy_from_user 和copy_to_user

5.Java层,分析总的一个流程图,4个角色的关系和代理的关系。从一个activityManager开始!

6.数据流向?

1642653981295-7oj.png

  1. 流程总结:

[图片上传失败...(image-9fecb0-1643110660184)]

1).Binder 驱动的全局链表 binder_procs 中插入服务端的信息(binder_proc 结构体,每个 binder_proc 结构体中都有 todo 任务队列),然后向 ServiceManager 的 svcinfo 列表中缓存一下注册的服务。

2). 有了服务端,客户端就可以跟服务端通讯了,通讯之前需要先获取到服务,拿到服务的代理,也可以理解为引用

获取服务端的方式就是通过 ServiceManager 向 svcinfo 列表中查询一下返回服务端的代理,svcinfo 列表就是所有已注册服务的通讯录,保存了所有注册的服务信息。

3).有了服务端的引用我们就可以向服务端发送请求了,通过 BinderProxy 将我们的请求参数发送给 ServiceManager,通过共享内存的方式使用内核方法 copy_from_user() 将我们的参数先拷贝到内核空间,这时我们的客户端进入等待状态,然后 Binder 驱动向服务端的 todo 队列里面插入一条事务,执行完之后把执行结果通过 copy_to_user() 将内核的结果拷贝到用户空间(这里只是执行了拷贝命令,并没有拷贝数据,binder只进行一次拷贝),唤醒等待的客户端并把结果响应回来,这样就完成了一次通讯。

2.重要的几个方法:open ,mmap ,iocontro。特别是mmap内存映射

内存的拷贝次数有几次?

肯定不是 1 次,服务端把数据拷到内核再拷回去 2 次,客户端把数据拷到内核再拷回去 2 次,还有一次是客户端把数据拷到服务端映射的内存中,还有一次是从服务端的映射内存拷贝到驱动层创建的内存。6 次拷贝

[图片上传失败...(image-36616-1643110660185)]

一次拷贝的过程:

发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内(这一次拷贝),核缓存区,

由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

Binder在一次拷贝中的作用则是怎么样的?

连接 两个进程,实现了mmap()系统调用,主要负责 创建数据接收的缓存空间 & 管理数据接收缓存

3、唤醒流程图,bp和br 解析

内核层内核层的通信都是通过ioctl来进行的,client打开一个ioctl,进入到轮询队列,一直阻塞直到时间到或者有消息。

[图片上传失败...(image-f9baa9-1643110660185)]

流程:

1.开始servermanager处于等待状态

2.然后media进程发送BP_transation

3.binder收到后发送br_transation。把servermanager唤醒,同时br_transtation_complete发送给客户端

4.客户端收到指令后,客户端处于等待的状态!

5.同上serverManager收到指令后。开始执行执行完后发送br_replay给binder驱动。同时还会发送

br_transtation_complete给servermanager。binder驱动发送br_rep给客户端唤醒

问题:

.是先serverManager在进行循环等待吗?然后客户端进行请求add吗?

4.binder_thread_write 和 binder_thread_read 和 copy_from_user 和copy_to_user 关系

binder_thread_write :主要作用:

binder_thread_read :主要作用:

copy_from_user :主要作用:

copy_to_user :主要作用:

调用流程:

binder_ioctl

    binder_ioctl_write_read

                  copy_from_user

                   binder_thread_write

binder_thread_read

                    copy_to_user

binder_thread_write里面又会调用 copy_from_user

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)
{
return_error == BR_OK) {

       switch (cmd) {
       ...
       case BC_TRANSACTION:
       case BC_REPLY: {
           struct binder_transaction_data tr;
           // 把数据从用户空间拷贝到内核空间 binder_transaction_data 
           if (copy_from_user(&tr, ptr, sizeof(tr)))

           binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
           break;
       }
   }
   return 0;

binder_thread_read里面又会调用copy_to_user

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)
{
   // 开始不断循环读取数据 
       switch (w->type) {
       case BINDER_WORK_TRANSACTION: {

       ptr += sizeof(uint32_t);
       //  把数据拷贝到用户空间
       if (copy_to_user(ptr, &tr, sizeof(tr)))
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;
   // 进入休眠直到中断被唤醒
   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);

   switch (cmd) {
   case BINDER_WRITE_READ:
       ret = binder_ioctl_write_read(filp, cmd, arg, thread);

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 的结构体对象地址

   struct binder_write_read bwr;

   // 将用户空间的 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);
       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;

   // 如果线程事务栈和 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;
}来源: https://www.jianshu.com/p/4472c7dabe6c

[图片上传失败...(image-c656fd-1643110660184)]

5.java层调用

[图片上传失败...(image-f837a8-1643110660184)]

但是上面还有个问题就是client和service要直接和binder driver打交道,但是实际上client和service并不想知道binder相关协议,所以进一步client通过添加proxy代理,service通过添加stub来进一步处理与binder的交互。

[图片上传失败...(image-b7ea4c-1643110660184)]

这样的好处是client和service都可以不用直接去和binder打交道。上面的图好像已经很完善了,但是Android系统更进一步封装,不让client知道Binder的存在,Android系统提供了Manager来管理client。如下图:

[图片上传失败...(image-14601e-1643110660184)]

这样client只需要交给manager来管理就好了,根本就不用关心进程通信相关的事,关于manager其实是很熟悉的,比如说activity的就是由ActivityManager来控制的,ActivityManager是通过Binder获取ActivityManagerService来控制activity的。这样就不用我们自己来使用Binder来ActivityManagerService通信了。

在service和binder之间还有一个contextManager,也就是serviceManage

总结流程:

注册服务过程:就是前面4篇文章讲的

第一步: service通过调用serviceManager中的addService方法,然后调用ServiceManagerNative类中的addservice(name)方法。

第二步: ServiceManagerNative会通过Binder发送一条SVG_MGR_ADD_SERVICE的指令,然后通过svcmgr_handler()调用do_add_service()方法往svc_list中添加相应的service。

重点:所有的服务都要先注册到svc_list中才能被client调用到。svc_list以linkedlist的形式保存这些服务。

获取服务过程:

第一步:client要请求服务,比如说在activity中调用context.getSystemService()方法,这个时候serviceManager就会使用getService(name),然后就会调用到native层中的ServiceManagerNative类中的getService(name)方法。

第二步:ServiceManagerNative会通过Binder发送一条SVG_MGR_GET_SERVICE的指令,然后通过svcmgr_handler()调用do_find_service()方法去svc_list中查找到相关的service。

第三步:查找到相应的服务后就会通过Binder将服务传给ServiceManagerNative,然后传给serviceManager,最后client就可以使用了。

注意: 服务实在svclist中保存的,svclist是一个链表,因此客户端调用的服务必须要先注册到svclist中。

你可能感兴趣的:(05. Android Binder图解 小米权威系统专家 解析binder总结调用流程 (安卓12))