美文网首页
Android底层:通熟易懂的分析binder--3. 探究bi

Android底层:通熟易懂的分析binder--3. 探究bi

作者: 牛晓伟 | 来源:发表于2020-12-20 15:00 被阅读0次

    ​前言

    Android底层:通熟易懂的分析binder--3. 探究binder全流程通信之请求篇已经把请求流程分析完毕了,接下来分析回复流程。

    本篇内容

    1. 方法调用栈
    2. 收到回复数据并发送
    3. 回复数据到达driver层
    4. server收尾工作
    5. client收到回复数据
    6. 全流程总结

    1.方法调用栈

    在分析回复流程之前需要把请求流程后,各个环节现在所处的方法调用栈讲清楚,这样可以作为我们后面分析的基础。

    分别用clientInvokeStack(client进程方法调用栈),serverInvokeStack(server进程方法调用栈),clientDriInvokeStack(“driver层client代理“方法调用栈),serverDriInvokeStack(”driver层server代理“方法调用栈)表示方法调用栈,位于方法栈前面的就是最近调用的方法。

    serverInvokeStack的方法有:

    1.server.methodXX(“真正”方法执行者)
    2.Stub的onTransact
    3.JavaBBinder的onTransact
    4.BBinder的transact
    5.IPCThreadState的execCommand
    6.IPCThreadState的getAndExecuteCommand
    7.IPCThreadState的joinThreadPool

    serverDriInvokeStack中暂时没有任何方法,因为driver层把请求数据传递给上层后就结束了。

    clientDriInvokeStack的方法有:

    1.binder_thread_read (进入等待状态,等待回复消息)
    2.binder_ioctl_write_read
    3.binder_ioctl

    clientInvokeStack的方法有:

    1.IPCThreadState::self()-> waitForResponse(进入等待状态,等待回复消息)
    2.IPCThreadState::self()->transact
    3.BpBinder::transact
    4.android_os_BinderProxy_transact (android_util_Binder.cpp)
    5.binderProxy.transactNative
    6.binderProxy.transact
    7.serverProxy.methodXX

    在分析回复流程中,其实是上面的方法栈的方法出栈的过程,因此上面的方法栈会伴随着我们的分析流程

    2.收到回复数据并发送

    发生于server进程

    下面分析用到了serverInvokeStack方法调用栈。

    收到回复数据是指server.methodXX方法已经处理完,并且把回复数据写入reply(Parcel)中,native层收到了这个回复数据,发送回复数据是指native层开始把回复发送给client。

    2.1 收到回复数据

    先上Stub代码

    public static abstract class Stub extends android.os.Binder implements IXXX {
            private static final java.lang.String DESCRIPTOR
                            = "...IXXX";
    /**
              * Construct the stub at attach it to the interface.
              */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    ​
            @Override
            public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,
                    int flags) throws android.os.RemoteException {
                switch (code) {
                  case TRANSACTION_methodXX: {
                        data.enforceInterface(descriptor);
                        // xxx 代表某一类型, data.readXXX代表读取参数
                        xxx _result = this.methodXX(data.readXXX());
                        reply.writeNoException();
                        reply.writeInt(_result);
                        return true;
                    }
                }
            }  
     }
    

    上面代码,Stub的子类对象(server)的methodXX方法处理完毕后,把回复数据写入reply(Parcel)中,这两方法从serverInvokeStack中出栈,进而进入JavaBBinder的onTransact和BBinder的transact方法,这两方法也基本没做啥处理,直接返回出栈,那现在serverInvokeStack中的方法有:

    1.IPCThreadState的execCommand
    2.IPCThreadState的getAndExecuteCommand
    3.IPCThreadState的joinThreadPool

    那我们进入execCommand方法,看下它未执行完的代码

    status_t IPCThreadState::executeCommand(int32_t cmd)
    {
        BBinder* obj;
        RefBase::weakref_type* refs;
        status_t result = NO_ERROR;
    ​
        switch ((uint32_t)cmd) {
    ​
        省略代码...
    ​
        case BR_TRANSACTION:
            {
                省略代码...
    ​
                // ptr有值,进入这
                if (tr.target.ptr) {
                    if (reinterpret_cast<RefBase::weakref_type*>(
                            tr.target.ptr)->attemptIncStrong(this)) {
                        // tr.cookie其实就是BBinder对象,这时候开始调用BBinder的transact方法,这是通往上层的入口
                        error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                                &reply, tr.flags);
                        reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                    } else {
                        error = UNKNOWN_TRANSACTION;
                    }
    ​
                } else {
                  省略代码...
                }
    ​
               // 同步请求则开始把结果返回
                if ((tr.flags & TF_ONE_WAY) == 0) {
                    if (error < NO_ERROR) reply.setError(error);
                    sendReply(reply, 0);
                } else {
                   省略代码...
                }
                省略代码...
    ​
            }
            break;
    ​
        省略代码...
        }
        return result;
    }
    

    上面代码,执行完

    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                                &reply, tr.flags);
    

    后,若没有error,并且是同步请求,则调用sendReply发送回复数据,reply中存储了回复数据,sendReply方法入栈,那现在serverInvokeStack中的方法有:

    1.IPCThreadState的sendReply
    2.IPCThreadState的execCommand
    3.IPCThreadState的getAndExecuteCommand
    4.IPCThreadState的joinThreadPool

    2.2 发送回复数据

    进入sendReply方法,看下它的代码

    status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
    {
        status_t err;
        status_t statusBuffer;
      // 把数据写入binder_transaction_data中,准备写入driver层
        err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
        if (err < NO_ERROR) return err;
      // waitForResponse中最终调用talkWithDriver方法把数据传递给driver层
        return waitForResponse(NULL, NULL);
    }
    

    上面代码把cmd为BC_REPLY(这时候给driver层的命令是BC_REPLY),reply等写入binder_transaction_data中后,调用waitForResponse方法,注意它的参数是null,null。waitForResponse调用talkWithDriver方法后把回复数据写入driver层,waitForResponse进入等待状态(等待driver层返回命令)

    waitForResponse方法入栈,那现在serverInvokeStack中的方法有:

    1.IPCThreadState的waitForResponse(进入等待状态)
    2.IPCThreadState的sendReply
    3.IPCThreadState的execCommand
    4.IPCThreadState的getAndExecuteCommand
    5.IPCThreadState的joinThreadPool

    小结

    到此server进程已经把回复数据发送给了driver层,server进程内的binder线程进入等待状态,停留于waitForResponse方法,后面还会进入这方法。

    3.回复数据到达driver层

    发生于“driver层server代理”

    我使用“driver层xx代理”来代表在driver层中记录的上层进程的信息(binder_proc,binder_thread等)的总称,xx是上层进程的名字(实在没想出一个好名字)。“driver层client代理”与client进程是对应关系,同理“driver层server代理”与server进程是对应关系。

    下面分析用到了serverDriInvokeStack方法调用栈。

    下面的流程分析起来就特别容易了,和请求流程基本是一模一样的,因此只把不一样的地方着中介绍下。

    和请求流程一样binder_ioctl,binder_ioctl_write_read,binder_thread_write方法入serverDriInvokeStack方法调用栈中。

    binder_thread_write

    进入binder_thread_write方法,看下回复相关代码

    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)
    {
      省略代码
    ​
      // thread->return_error == BR_OK 这个很关键, BR_OK代表还没把信息返回给上层
      while (ptr < end && thread->return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))
          return -EFAULT;
        省略代码...
        switch (cmd) {
    ​
        case BC_REPLY: {
          struct binder_transaction_data tr;
    ​
          // 拷贝 binder_transaction_data
          if (copy_from_user(&tr, ptr, sizeof(tr)))
            return -EFAULT;
          ptr += sizeof(tr);
          binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
          break;
        }
    ​
        省略代码...
      }
      return 0;
    }
    

    因为从上层传递的cmd是BC_REPLY,因此进入binder_transaction方法,是不是和BC_TRANSACTION cmd的流程是一样的。binder_transaction方法进入serverDriInvokeStack方法调用栈中。

    3.1 查找接收回复数据的“目标”

    这里的“目标”当然是“driver层client代理”,下面来看下查找过程,查找代码依然在binder_transaction方法中,只展示与回复相关代码

    static void binder_transaction(struct binder_proc *proc,
                 struct binder_thread *thread,
                 struct binder_transaction_data *tr, int reply)
    {
      // t里面的数据会存放到目标binder_proc或binder_thread的队列中
      struct binder_transaction *t;
        // tcomplete用来告诉client数据已经成功的交给了目标
      struct binder_work *tcomplete;
        // 用来对binder服务,binder服务引用做转换
      binder_size_t *offp, *off_end;
      binder_size_t off_min;
    ​
      // 目标
      struct binder_proc *target_proc;
      struct binder_thread *target_thread = NULL;
      struct binder_node *target_node = NULL;
      struct list_head *target_list;
      wait_queue_head_t *target_wait;
    ​
      // 回复相关
      struct binder_transaction *in_reply_to = NULL;
    ​
      省略代码...
    ​
    ​
      // 回复结果
      if (reply) {
            // transaction_stack存放 事务栈, 主要用于回复阶段
        in_reply_to = thread->transaction_stack;
        if (in_reply_to == NULL) {
          binder_user_error("%d:%d got reply transaction with no transaction stack\n",
                proc->pid, thread->pid);
          return_error = BR_FAILED_REPLY;
          goto err_empty_call_stack;
        }
        binder_set_nice(in_reply_to->saved_priority);
            // 判断是否是一样的binder_thread
        if (in_reply_to->to_thread != thread) {
          binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
            proc->pid, thread->pid, in_reply_to->debug_id,
            in_reply_to->to_proc ?
            in_reply_to->to_proc->pid : 0,
            in_reply_to->to_thread ?
            in_reply_to->to_thread->pid : 0);
          return_error = BR_FAILED_REPLY;
          in_reply_to = NULL;
          goto err_bad_call_stack;
        }
        thread->transaction_stack = in_reply_to->to_parent;
            // 找到target_thread
        target_thread = in_reply_to->from;
        if (target_thread == NULL) {
          return_error = BR_DEAD_REPLY;
          goto err_dead_binder;
        }
            // 容错判断
        if (target_thread->transaction_stack != in_reply_to) {
          binder_user_error("%d:%d got reply transaction with bad target transaction stack %d, expected %d\n",
            proc->pid, thread->pid,
            target_thread->transaction_stack ?
            target_thread->transaction_stack->debug_id : 0,
            in_reply_to->debug_id);
          return_error = BR_FAILED_REPLY;
          in_reply_to = NULL;
          target_thread = NULL;
          goto err_dead_binder;
        }
            // 找到目标binder_proc
        target_proc = target_thread->proc;
      }
    }
    

    上面代码很简单,主要做了以下几件事情:

    1.因为当前是reply是true,进入reply环节
    2.thread->transaction_stack保存了请求时的事务
    3.查找“目标”binder_thread, binder_proc

    查找到“目标”后,与请求流程一样,会把回复数据拷贝(对BBinder,BpBinder进行转换)后组装成type为BINDER_WORK_TRANSACTION,的binder_work放入“目标”的todo队列中。同时把type为

    BINDER_WORK_TRANSACTION_COMPLETE的binder_work放入当前binder_thread的todo队列中。除此之外回复流程还做了从thread->transaction_stack中移除reply事务的操作。

    binder_transaction,binder_thread_write方法从serverDriInvokeStack中出栈。

    小结

    到此回复数据已经放入了“目标”的binder_thread(“driver层client代理”)的todo队列中(注意此时的binder_transaction->buffer->target_node为null)

    “driver层server代理”的binder_thread的todo队列放入type为BINDER_WORK_TRANSACTION_COMPLETE的binder_work(它的binder_transaction为null)。

    因此这个时候形成了“driver层client代理”与“driver层server代理”并行执行的情况。我们先把“driver层server代理”的流程分析完毕,再来专注分析“driver层client代理”这条线。

    serverDriInvokeStack方法调用栈包含的方法有:

    1.binder_ioctl
    2.binder_ioctl_write_read

    4.server收尾工作

    发生于server进程和“driver层server代理”

    会用到serverInvokeStack,serverDriInvokeStack方法调用栈。

    “driver层server代理”把回复数据传递给了“driver层client代理”,server进程还需要做一些收尾工作。

    4.1 发送“complete”事件给上层

    与处理请求的逻辑一样,“driver层server代理”收到BINDER_WORK_TRANSACTION_COMPLETE的binder_wrok后,会发送BR_TRANSACTION_COMPLETE的cmd给上层,binder_thread_read,binder_ioctl_write_read,binder_ioctl方法先后从serverDriInvokeStack出栈,serverDriInvokeStack方法调用栈中没有任何方法,“driver层server代理”的处理流程结束。

    4.2 server收尾工作

    收到BR_TRANSACTION_COMPLETE cmd后进入server的收尾工作。先回顾下

    serverInvokeStack中的方法有哪些:

    1.IPCThreadState的waitForResponse(进入等待状态)
    2.IPCThreadState的sendReply
    3.IPCThreadState的execCommand
    4.IPCThreadState的getAndExecuteCommand
    5.IPCThreadState的joinThreadPool

    咱们依次从这些方法入手,看执行收尾工作的流程

    回到waitForResponse,先看下相关代码

    case BR_TRANSACTION_COMPLETE:
                if (!reply && !acquireResult) goto finish;
                break;
    

    因为reply和acquireResult都是null,因此直接finish。从serverInvokeStack中出栈

    其他的sendReply,execCommand,getAndExecuteCommand方法也先后从serverInvokeStack中出栈,

    回到joinThreadPool,看下相关代码

    void IPCThreadState::joinThreadPool(bool isMain)
    {
    ​
        省略代码...
    ​
        status_t result;
        do {
            processPendingDerefs();
            // now get the next command to be processed, waiting if necessary
            result = getAndExecuteCommand();
    ​
            if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
                ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
                      mProcess->mDriverFD, result);
                abort();
            }
    ​
            // Let this thread exit the thread pool if it is no longer
            // needed and it is not the main process thread.
            if(result == TIMED_OUT && !isMain) {
                break;
            }
        } while (result != -ECONNREFUSED && result != -EBADF);
    ​
        省略代码...
    ​
        mOut.writeInt32(BC_EXIT_LOOPER);
        talkWithDriver(false);
    }
    

    若getAndExecuteCommand方法执行的结果result正常,则进入

    if(result == TIMED_OUT && !isMain) {
                break;
    }
    

    这块代码分为两个逻辑:

    1.若isMain为false(不是binder主线程),则从do循环中跳出,往mOut中写入BC_EXIT_LOOPER cmd通过talkWithDriver方法传递给driver层,注意talkWithDriver方法这时候的参数是false,false代表不需要等待driver层的返回数据,只要把数据成功发送给driver层后就直接结束。当前的binder线程结束,joinThreadPool方法从serverInvokeStack中出栈,serverInvokeStack中没有任何方法。

    2.若isMain为true(binder主线程),则继续与driver层通信,driver层最终进入binder_thread_read方法进入等待状态,监听新的事务。

    小结

    到此,binder进程通信中的server进程回复阶段处理完毕,server进程中的binder线程若为非主线程,则一般会直接结束,否则浪费资源;若为主线程,则继续与driver层通信,等待着新的事务(最起码得有一个binder线程来接收事务)。

    5.client收到回复数据

    发生于client进程和“driver层client代理”

    会用到clientInvokeStack,clientDriInvokeStack方法调用栈。

    clientDriInvokeStack的方法有:

    1.binder_thread_read (进入等待状态,等待回复消息)
    2.binder_ioctl_write_read
    3.binder_ioctl

    5.1 “driver层client代理”发送回复数据给上层

    3.1 节 中"driver层client代理“的binder_thread的todo队列中收到回复binder_work后,binder_thread_read方法被唤醒,拿到回复binder_transaction数据后把其中相关的数据赋值给binder_transaction_data结构体,把cmd为BR_REPLY和

    binder_transaction_data数据发送给上层,binder_thread_read,binder_ioctl_write_read,binder_ioctl方法先后从clientDriInvokeStack出栈,clientDriInvokeStack没有任何方法。

    5.2 client收到回复数据

    先来看下clientInvokeStack方法调用栈,包含的方法有哪些:

    1.IPCThreadState::self()-> waitForResponse(进入等待状态,等待回复消息)
    2.IPCThreadState::self()->transact
    3.BpBinder::transact
    4.android_os_BinderProxy_transact (android_util_Binder.cpp)
    5.binderProxy.transactNative
    6.binderProxy.transact
    7.serverProxy.methodXX

    咱们依次从这些方法入手,来分析收到回复数据流程。

    client native层在waitForResponse方法中处于等待状态,等待回复数据,看下相关代码

    status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
    ​
       uint32_t cmd;
        int32_t err;
    ​
        while (1) {
        省略代码...
    ​
        case BR_REPLY:
                {
                    binder_transaction_data tr;
                    err = mIn.read(&tr, sizeof(tr));
                    ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                    if (err != NO_ERROR) goto finish;
                    // 是回复
                    if (reply) {
                        if ((tr.flags & TF_STATUS_CODE) == 0) {
                            reply->ipcSetDataReference(
                                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                                tr.data_size,
                                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                                tr.offsets_size/sizeof(binder_size_t),
                                freeBuffer, this);
                        } else {
                            err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                            freeBuffer(NULL,
                                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                                tr.data_size,
                                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                                tr.offsets_size/sizeof(binder_size_t), this);
                        }
                    } else {
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                        continue;
                    }
                }
                goto finish;
      }
      省略代码...
    }
    

    因为driver层传递的cmd是BR_REPLY,进入该case,最终进入下面的代码流程

    if (reply) {
        if ((tr.flags & TF_STATUS_CODE) == 0) {
           reply->ipcSetDataReference(
              reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
              tr.data_size,
              reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
              tr.offsets_size/sizeof(binder_size_t),
              freeBuffer, this);
         }
     }   
    

    reply(Parcel对象)调用ipcSetDataReference方法把driver层传递的回复数据写入reply中,进而结束waitForResponse方法的调用,该方法执行出栈操作。

    IPCThreadState::self()->transact,BpBinder::transact,android_os_BinderProxy_transact,binderProxy.transactNative,binderProxy.transact先后执行出栈操作,最终在serverProxy.methodXX方法中,把reply回复数据读取出来。

    clientInvokeStack内没有任何方法。

    至此,整个回复流程结束,因为有请求流程作为基础,因此回复流程分析起来相对简单了很多。

    6.全流程总结

    到此binder进程通信全流程分析完毕,我用一张图来总结下整个流程


    Android binder简版 (1).jpg

    图中解析

    1.黑色直线箭头代表client请求server的过程,绿色直线箭头代表server处理完毕返回结果给client的过程
    2.红色的框和红色字体代表每层之间传递的数据的变化
    3.粉色框代表哪层,比如framework层

    1. 蓝色字体是对关键信息的说明
    2. client是c/s中的client是一个进程,同理server就是server端,也是一个进程

    上面这张图看上去确实比较复杂(即时我去掉了一些无关紧要的流程),它展示了client请求server服务及server把结果返回给client的过程。在这个过程中,每层(app,framework,jni,native,driver)之间协议的变化,每层之间是怎么联系起来的,native与driver层都做了哪些处理等。

    相关文章

      网友评论

          本文标题:Android底层:通熟易懂的分析binder--3. 探究bi

          本文链接:https://www.haomeiwen.com/subject/hxlknktx.html