查看原文
其他

Android底层:通熟易懂的分析binder--3. 探究binder全流程通信之回复篇

我是技术男 牛晓伟
2024-08-24


前言


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


本篇内容



  1. 方法调用栈

  2. 收到回复数据并发送

  3. 回复数据到达driver层

  4. server收尾工作

  5. client收到回复数据

  6. 全流程总结



01


方法调用栈


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


分别用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


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


02


收到回复数据并发送




发生于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方法,后面还会进入这方法。



03


回复数据到达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



04


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线程来接收事务)。




05


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内没有任何方法。


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



06


全流程总结



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


图中解析


1.黑色直线箭头代表client请求server的过程,绿色直线箭头代表server处理完

毕返回结果给client的过程

2.红色的框和红色字体代表每层之间传递的数据的变化

3.粉色框代表哪层,比如framework层

4. 蓝色字体是对关键信息的说明

5. client是c/s中的client是一个进程,同理server就是server端,也是一个进程


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












继续滑动看下一个
牛晓伟
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存