Binder机制之一次响应的故事

作者: CoorChice | 来源:发表于2017-08-07 10:34 被阅读1375次
    image

    Binder系列第一篇:《从getSystemService()开始,开撸Binder通讯机制》http://www.jianshu.com/p/1050ce12bc1e

    Binder系列第二篇:《能用【白话文】来分析Binder通讯机制?》http://www.jianshu.com/p/fe816777f2cf

    Binder系列第三篇:《Binder机制之一次响应的故事》http://www.jianshu.com/p/4fba927dce05

    在上一篇文章《能用【白话文】来分析Binder通讯机制?》http://www.jianshu.com/p/fe816777f2cf 中,我们已经跑通了一遍客户端和Binder内核的一次通讯。即客户端请求一次通讯,到Binder内核给予客户端一个回应。没有印象的通讯可能需要回头再回顾一下了。本篇CoorChice将接着上一次由于篇幅太长而无法继续讲的回应流程。

    事实上,对于我们一开始发起的通讯请求status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0) 而言,我们只进行了这次请求处理流程的一半。还记得这个请求是那发送的和干什么的吗?在上一篇流程起始的地方再回顾下哦。

    image

    好了,接着上次来!

    首先,在此放出Binder通讯机制的完整流程图!整个流程很长,而且有些绕人,同学们一定要经常回过来看图啊。

    image

    好吧,再放个高清的下载地址:http://ogemdlrap.bkt.clouddn.com/Binder%E8%BF%9B%E9%98%B6%E5%AE%8C%E6%95%B4.png

    通讯发起端的休眠ING

    上一次CoorChice说到,发起端进程在收到Binder内核响应的BR_TRANSACTION_COMPLETE后,再一次的进入了IPCThreadState::talkWithDriver()函数。这就意味着发起端需要和Binder内核再通讯一次。

    而这一次通讯,由于没有需要发送的的数据,所以我们直接看Binder内核中的binder_thread_read()函数,在图中找到对应的位置进入状态了啊。至于是怎么到这个函数的,你可能需要回顾下上一篇中的内容了,它们基本是一样的,只是跳过了binder_thread_write()而已。

    那么我们看看这一次发起端进程在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)
    {
        ...
        //检查是否需要等待进程work完成
        wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
        ...
        if (wait_for_proc_work) {
            ...
        } else {
            if (non_block) {
                ...
            } else
                //如果不需要等待,进程进入休眠,等待唤醒
                ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
        }
        ...
    

    我们只看关键的。还记得上一篇在第8步binder_transaction()函数中,我们最后把添加到目标进程中的事务t同时赋值给了发送端进程的thread->transaction_stack。所以此处的thread->transaction_stack是不为NULL的。从而wait_for_proc_work就为false,于是就执行到了ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));这一句,然后发送端进程就进入了休眠。目的是为了等待目标进程处理完请求后给予它响应。

    来自目标进程的回应

    在此之前,我们一直关注的都是发送端进程的流程。现在,发送端进程进入了休眠,是时候来看一看本次通讯的目标进程了。在上一篇的第8步binder_transaction()函数中,我们把发送端的诉求封装进了一个事务t中,然后把事务t添加到了目标进程的事务队列中,最后把目标进程唤醒了。

    接下来我们看看目标进程被唤醒之后都干了些什么?

    第一步 从沉睡中醒来

    这次我们关注图中的紫色流程线,从编号为1的线开始看。

    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)
    {
        ...
        ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        ...
        if (ret)
            return ret;
        ...
    }
    

    由于是通过正常的wake_up_interruptible()函数进行唤醒,所以返回的ret = 0。接着,目标进程会进入switch,根据之前设置的cmd执行相应逻辑。

    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)
    {
        ...
            while (1) {
            
            uint32_t cmd;
            struct binder_transaction_data tr;
            struct binder_work *w;
            struct binder_transaction *t = NULL;
            if (!list_empty(&thread->todo)) {
                //获取线程的work队列
                w = list_first_entry(&thread->todo, struct binder_work, entry);
            } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
                //获取从进程获取work队列
                w = list_first_entry(&proc->todo, struct binder_work, entry);
            } else {
                //没有数据,则返回retry
                if (ptr - buffer == 4 &&
                    !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                    goto retry;
                break;
            }
            
            switch (w->type) {
                case BINDER_WORK_TRANSACTION:
                    //根据队列中的work获取包含该work的事务
                    t = container_of(w, struct binder_transaction, work);
                    break;
                    
                ...
            }
        ...
    }
    

    这部分需要时刻结合上一篇中的第8步binder_transaction()函数来看,很多参数都是来自于那的。

    根据之前的逻辑,我们并没有给目标进程的thread->todo赋值,而是给它的proc->todo赋了值。因此,我们从proc->todo的任务队列中取出之前添加的任务。因为任务类型是BINDER_WORK_TRANSACTION,所以swicth中走了BINDER_WORK_TRANSACTION的逻辑,获取到通讯事务t。

    接着看下面的逻辑。

    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)
    {
        ...
            while (1) {
            
            uint32_t cmd;
            struct binder_transaction_data tr;
            struct binder_work *w;
            struct binder_transaction *t = NULL;
            ...
            //只有BINDER_WORK_TRANSACTION命令,即t不为空才能继续往下执行
            if (!t)
                continue;
            
            //判断事物t中是否有目标进程的Binder实体
            if (t->buffer->target_node) {
                struct binder_node *target_node = t->buffer->target_node;
                tr.target.ptr = target_node->ptr;
                tr.cookie =  target_node->cookie;
                t->saved_priority = task_nice(current);
                ...
                cmd = BR_TRANSACTION;  //设置命令为BR_TRANSACTION
            } else {
                tr.target.ptr = NULL;
                tr.cookie = NULL;
                cmd = BR_REPLY; //设置命令为BR_REPLY
            }
            //给刚刚定义的事务信息体设置成员的值
            tr.code = t->code;
            tr.flags = t->flags;
            tr.sender_euid = t->sender_euid;
            ...
    
        ...
    }
    

    对于if(!t),很明显,我们是有事务t,所以代码会接着往下执行。

    我们之前有t->buffer->target_node = target_node;,所以这里t->buffer->target_node)是不为空的。然后,取出这个target_node,也就是目标进程的Binder实体。

    接着,把目标进程的Binder实体的引用和cookie缓存保存到新定义的通讯数据tr中。然后,设置命令为BR_TRANSACTION,这些都需要记好了哦。

    image
    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)
    {
        ...
            tr.data_size = t->buffer->data_size;
            tr.offsets_size = t->buffer->offsets_size;
            tr.data.ptr.buffer = (void *)t->buffer->data +
            proc->user_buffer_offset;
            tr.data.ptr.offsets = tr.data.ptr.buffer +
            ALIGN(t->buffer->data_size,
                  sizeof(void *));
            
            //将cmd和数据写回用户空间的mIn
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            //刚刚赋值好的事务信息写会用户空间的mIn
            if (copy_to_user(ptr, &tr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            //删除本次work
            list_del(&t->work.entry);
            ...
            kfree(t); //通信完成,则运行释放
            break;
        }
        ...
        return 0;
    
        ...
    }
    

    接下来就是把事务t中的数据储存到新定义的通讯数据tr中。然后把刚刚的cmd拷贝到目标进程空间,再把新定义的通讯数据tr也拷贝到目标进程空间。最后删除本次事务,释放内存,结束该函数。

    第三步 退出ioctl()函数

    上一步函数结束后,函数基本都是一路退出,直到Binder内核的binder_ioctl()函数。至此,由于我们的目标进程是ServiceManager,所以binder_ioctl()函数执行完后,返回的是/frameworks/native/cmds/servicemanager/binder.c中的binder_looper()。所以,从这里开始,我们转为关注黄色的流程线。

    至于为什么会回到binder_looper()函数,这与ServiceManager的机制有关。由于它在系统中既是一个Service,又是一个守护进程,它需要不断的循环等待Client的请求。处理完一个请求,就再次进入循环等待下一个请求。而这个函数就是为它提供这种能力的。这里我们先不讨论ServiceManager工作原理,继续把目光聚焦在我们本次通讯的流程上。

    我们进入/frameworks/native/cmds/servicemanager/binder.c中的binder_looper()看看在这个流程中它有什么用?

    void binder_loop(struct binder_state *bs, binder_handler func)
    {
        ...
        
        for (;;) {
            bwr.read_size = sizeof(readbuf);
            bwr.read_consumed = 0;
            bwr.read_buffer = (uintptr_t) readbuf;
            ...
            //和binder通讯,等待Client链接
            res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
            ...
            //解析Client的数据
            res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
            ...
        }
    }
    

    可以看到,ServiceManager通过一个无限循环,反复的进行ioctl()binder_parse()操作。还记得第一步中,在没有消息时,ServiceManager会在Binder内核的binder_thread_read()函数中被休眠。

    这里,从ioctl()函数出来后,表明ServiceManager收到了Binder内核传递过来的信息,所以下一步就是要多信息进行处理。

    第四步 binder_parse()处理信息

    int binder_parse(struct binder_state *bs, struct binder_io *bio,
                     uintptr_t ptr, size_t size, binder_handler func)
    {
        int r = 1;
        uintptr_t end = ptr + (uintptr_t) size;
        
        while (ptr < end) {
            uint32_t cmd = *(uint32_t *) ptr;
            ptr += sizeof(uint32_t);
            ...
    }
    

    首先关注参数ptr,它是ServiceManager传到内核空间的信使binder_write_read bwr的读取缓冲区read_buffer。在第二步binder_thread_read()中,我们在这个buffer中装了一个命令BR_TRANSACTION和一个事物数据tr。忘了快回过头看看吧。

    end变量可以指向这个缓冲区结束位置,而此时的ptr是指向缓冲区的头部。所以能够进入while循环。接着从缓冲区中取出刚刚存入的命令cmd,然后将ptr移动一个cmd的距离。

    int binder_parse(struct binder_state *bs, struct binder_io *bio,
                     uintptr_t ptr, size_t size, binder_handler func)
    {
        ...
        
        while (ptr < end) {
            ...
            switch(cmd) {
                case BR_NOOP:
                    break;
                    //通知ServiceManager通讯事务完成
                case BR_TRANSACTION_COMPLETE:
                    break;
                    ...
                    //执行事务
                case BR_TRANSACTION: {
                    //获取通讯事务数据
                    struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
                    ...
                    if (func) {
                        unsigned rdata[256/4];
                        // 用于保存"Binder驱动反馈的信息"
                        struct binder_io msg;
                        // 用来保存"回复给Binder驱动的信息"
                        struct binder_io reply;
                        int res;
                        // 初始化reply
                        bio_init(&reply, rdata, sizeof(rdata), 4);
                        // 根据txt(Binder驱动反馈的信息)初始化msg
                        bio_init_from_txn(&msg, txn);
                        // 消息处理
                        res = func(bs, txn, &msg, &reply);
                        // 反馈消息给Binder驱动
                        binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                    }
                    ptr += sizeof(*txn);
                    break;
                }
                case BR_REPLY: {
                    ...
                    break;
                }
                    ...
            }
        }
        
        return r;
    }
    

    前面说过Binder驱动传过来的cmd是BR_TRANSACTION类型,所以我们只看相关逻辑。

    BR_TRANSACTION中,首先要做的就是获得从Binder内核读取到的信息。可以看到,将参数ptr转换成了binder_transaction_data指针。接下来初始化两个binder_io结构,msg用来储存从Binder内核读取的信息,replay用来储存待会儿需要反馈给请求端的信息。然后通过bio_init_from_txn(&msg, txn)函数将从Binder内核读取的信息,存入msg中。

    然后我们看到,这里调用了一个fun()函数,它是一个函数指针,代表着svcmgr_handler()函数。不难看出,它就是ServiceManager用于处理Binder内核消息的函数。

    第五步 svcmgr_handler()处理请求消息

    int svcmgr_handler(struct binder_state *bs,
                       struct binder_transaction_data *txn,
                       struct binder_io *msg,
                       struct binder_io *reply)
    {
        struct svcinfo *si;
        uint16_t *s;
        size_t len;
        uint32_t handle;
        uint32_t strict_policy;
        int allow_isolated;
        ...
        
        //测试指令PING_TRANSACTION直接返回
        if (txn->code == PING_TRANSACTION)
            return 0;
        
        ...
    }
    

    首先我们知道,binder_transaction_data装有来自Binder内核的消息。它装有的消息基本就是请求发起进程发送的消息,也就是其发送的binder_transaction_data里的信息。这部分你需要看上一篇的第八步binder_transaction()和这一篇中的第一步binder_thread_read(),它们中关于binder_transaction_data结构的赋值逻辑需要理清楚。

    既然如此,那么我们就回过头看发起请求的进程在binder_transaction_data结构中装了什么信息。我们需要回到IPCThreadState::writeTransactionData()函数中,也就是初始化发送信使binder_transaction_data的地方。在这里,我们可以看到,信使中的code码是参数传进来的。我们沿着这条线往回找,可以发现,就是一开始我们发起这次Binder通讯的地方status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0) ,设置了code为IBinder::PING_TRANSACTION。真想大白,code为PING_TRANSACTION类型就什么也不干,直接退出这个函数。这就是为什么说我们发起这次请求只是测试一下ServiceManager是否已经注册的原因。

    实际上,如果code为其它情况的话,在这里会进行一些其它的逻辑。比如,添加一个Service或者查找获得一个Service。在后面遇到了再回过头了说。

    image

    第六步 binder_send_reply()发送处理结果

    上一步,service_manager.c中的svcmgr_handler()函数中,对请求端的请求进行了处理。然后函数退出,回到binder.cbinder_parse()继续执行。下一步就是给予请求端处理结果,即这一次ServiceManager要给请求端发送消息了。

    void binder_send_reply(struct binder_state *bs,
                           struct binder_io *reply,
                           binder_uintptr_t buffer_to_free,
                           int status)
    {
        struct {
            uint32_t cmd_free;
            binder_uintptr_t buffer;
            uint32_t cmd_reply;
            struct binder_transaction_data txn;
        } __attribute__((packed)) data;
        //把回复数据放到data中
        data.cmd_free = BC_FREE_BUFFER;
        data.buffer = buffer_to_free;
        data.cmd_reply = BC_REPLY;
        data.txn.target.ptr = 0;
        data.txn.cookie= 0;
        data.txn.code = 0;
        if (status) {
           ...
        } else {
            data.txn.flags = 0;
            data.txn.data_size = reply->data - reply->data0;
            data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
            data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
            data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
        }
        //发送响应
        binder_write(bs, &data, sizeof(data));
    }
    

    上面这段代码不长,它主要干了两个事:

    • 定义并初始化储存回复信息的data结构体。其中,cmd_replay是本次通讯的指令,后面进行什么逻辑,是更具这个值判断的。
    • 调用binder_write()函数,准备和Binder内核通讯,从而将回复信息传递给发送端。

    第七步 binder_write()发送回复信息

    int binder_write(struct binder_state *bs, void *data, size_t len)
    {
        struct binder_write_read bwr;
        int res;
        //初始化信使
        bwr.write_size = len;
        bwr.write_consumed = 0;
        bwr.write_buffer = (uintptr_t) data;
        bwr.read_size = 0;
        bwr.read_consumed = 0;
        bwr.read_buffer = 0;
        //通过Binder发送响应
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        ...
        return res;
    }
    

    啊哈,上面的binder_write_read结构体是不是很熟悉的样子?还记得IPCThreadState::talkWithDriver()中也构造了一个吗?它负责将需要发送的信息带到Binder内核,从而传递给通讯目标。

    和Binder内核的通讯同样是通过ioctl()函数进行的。这会让代码逻辑回到/drivers/staging/android/binder.c中的binder_ioctl()函数中。

    如果上一篇的流程你已经理清楚的话,后面的逻辑你因该都能猜到了。

    image

    第八步 在Binder内核中读写

    我们回到/drivers/staging/android/binder.c中的binder_ioctl()函数中,在图中找到对应位置哦!

    逻辑其实和上一篇读取发送端消息,然后在传输给接收端大致是差不多的。只是中间的一些cmd不同,某些步骤执行的操作有区别罢了。CoorChice在上一篇中也有提到。

    binder_ioctl()函数中,同样先获取到发送过来的消息参数,然后由于调用ioctl()的参数是BINDER_WRITE_READ,所以直接进入binder_ioctl_write_read()函数。大家在图中找到对应位置。在这个函数中,由于是发送消息,所以会进入binder_thread_write()函数。

    binder_thread_write()函数还是和之前差不多,先读取发送端的消息信息等,然后根据信使binder_write_readwrite_buffer中的cmd决定执行什么逻辑。而这个cmd的值你可以回到第六步binder_send_reply()中看看,它是BC_REPLY类型的。那么就简单了,和上一篇发送端逻辑一样,会进入到binder_transaction()函数中。

    binder_transaction()函数就是之前说的有些复杂的函数,大家在图中继续找到对应位置啊。通过上一篇的阅读,相信你已经知道了这个函数主要会根据发送端信息(此时为ServiceManager)和接收端(原本的发送端)的信息,构造两个事务分别添加到发送端和接收端进程的事务列表中。同样,一个事务是给发送端通知本次你发送的信息Binder内核已经转发给接收端了,另一个事务自然是将发送端的消息带到接收端中处理。

    binder_transaction()函数执行完后,对ServiceManager而言,由于执行的事务是告知ServiceManager本次Transication完成了,消息已经发送出去,即BINDER_WORK_TRANSACTION_COMPLETE命令,所以逻辑同上一篇的基本一致,CoorChice就不重复说了。总之函数一路执行完毕,然后回到/frameworks/native/cmds/servicemanager/binder.cbinder_parse()中。你可以在这个函数中看到:

    ...
     case BR_TRANSACTION_COMPLETE:
                    break;
    ...
    

    在图中找找对应位置哦。收到BR_TRANSACTION_COMPLETE指令后,直接break。然后binder_parse()函数执行完毕。代码逻辑回到第三步的binder_loop()中,由于有一个for循环,所以会再次执行到

    void binder_loop(struct binder_state *bs, binder_handler func)
    {
        ...
        
        for (;;) {
           ...
            //和binder通讯,等待Client链接
            res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
            ...
        }
    }
    

    这一次和Binder内核ioctl通讯,由于没数据要发,所以会一路进入/drivers/staging/android/binder.cbinder_thread_read()函数中。就在第一步醒来的地方再次进入休眠,等待下一次的唤醒。

    //休眠,等待下一次唤醒
    ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    

    至此,以我们本次通讯为例,ServiceManager的任务算是完成了。

    通讯发起端收到来自接收端的响应

    约定: 本次通讯中,通讯发起端称为C,ServiceManager称为S。

    下面筒靴们跟着紫色流程线看。

    image

    第一步 通讯发起端醒来

    开篇我们说过,通讯发起端在/drivers/staging/android/binder.cbinder_thread_read()函数中的``ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));`未知休眠了,那么这一次被唤醒的自然是我们通讯发起端。

    第二步 读取S端的响应信息

    同上面的逻辑一样,C在醒来后会从事务中读取来自S的响应信息。在binder_thread_read()函数中找到下面这段代码。

    ...
    //判断事物t中是否有目标进程的Binder实体
            if (t->buffer->target_node) {
                ...
            } else {
                tr.target.ptr = NULL;
                tr.cookie = NULL;
                cmd = BR_REPLY; //设置命令为BR_REPLY
            }
    ...
    //将cmd和数据写回用户空间的mIn
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            //刚刚赋值好的事务信息写会用户空间的mIn
            if (copy_to_user(ptr, &tr, sizeof(tr)))
                return -EFAULT;
    ...
    

    由于在binder_transaction()函数中,由于S的cmd为BC_REPLAY,所以走的是reply的逻辑。这意味着t->buffer->target_node为NULL。所以这一步,会走上面的逻辑。注意,此时cmd被设置成了BR_REPLY

    第三步 C读取S的响应

    上一步执行完毕后,C就一路退出,结束和Binder内核的通讯。即会从/frameworks/native/libs/binder/IPCThreadState.cppIPCThreadState::talkWithDriver()中退出,然后回到IPCThreadState::waitForResponse()中。

    上一步中,我们知道cmd被赋值为BR_REPLY,然后写到了C中。所以看看IPCThreadState::waitForResponse()中的switch逻辑吧,知道会往那执行了不?

    status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
    {
        ...
        while (1) {
            //真正和Binder驱动交互的是talkWithDriver()函数
            if ((err=talkWithDriver()) < NO_ERROR) break;
            ...
            //取出在内核中写进去的cmd命令
            cmd = mIn.readInt32();
            ...
            
            switch (cmd) {
                ...
                case BR_REPLY:
                {
                    binder_transaction_data tr;
                    //读取内核中写入的事务数据
                    err = mIn.read(&tr, sizeof(tr));
                    ...
                    if (reply) {
                        //通常这里的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);
                            ...
                        }
                    }
                    ...
                }
            }
        }
        ...
        return err;
    }
    

    没错,走的是获取响应信息的BR_REPLY逻辑。首先会把内核中传递过来的事务tr读取出来,由于前面tr是有值的,所以这里err = NO_ERROR。接着,reply在上一篇中说过,它是初始化过的,所以不为NULL,进入reply逻辑。tr.flags的值是来自与S的,具体在上面第六步的binder_send_reply()函数中,有这样一句data.txn.flags = 0。忘记了翻回去找找哦。因此,这里调用了reply的Parcel::ipcSetDataReference()函数,写入响应信息。然后该函数就该返回了。注意此时err仍然为NO_ERROR

    第四步 退出waitForResponse()函数

    上一步waitForResponse()函数返回了一个NO_ERROR,然后回到IPCThreadState::transact()函数中。接着,这个函数也没什么逻辑需要执行了,就将err返回。

    第五步 一次完整的通讯终于完成啦!

    IPCThreadState::transact()函数结束后,我们终于回到了最初的地方,那个通讯发起的地方

    sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
    {
        sp<IBinder> result;
        
        ...
        //在handle对应的BpBinder第一次创建时
        //会执行一次虚拟的事务请求,以确保ServiceManager已经注册
        status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);
        if (status == DEAD_OBJECT)
            //如果ServiceManager没有注册,直接返回
            return NULL;
        ...
    }
    

    此时,status为NO_ERROR,即ServiceManager已经注册了。

    总结

    • 抽出空余时间写文章分享需要动力,还请各位看官动动小手点个赞,给我点鼓励😄
    • 我一直在不定期的创作新的干货,想要上车只需进到我的【个人主页】点个关注就好了哦。发车喽~

    这一篇,我们终于通关了一遍Binder通讯机制的完整流程。确实很长!很长!很长!

    image

    其实大家可以看到,阅读源码时画一画流程图是很有必要的,特别是对这种复杂的源码。边看边画比较容易梳理清楚,不然流程太长会看着后面忘了前面的!有了图就可以随时回顾到忘记的地方。我们画图不一定要套用标准的流程图、过程图之类的,选择适合自己的方式就好。

    通过这3篇文章,我们从一次getSystemService()的调用开始,一步步深入到了Binder机制的核心中,最终明白了Binder机制到底是如何运作的,为什么它能够撑起整个Android系统。一个简单的接口调用,背后往往蕴藏着一套精心设计的逻辑,这就是编程的魅力所在吧。

    看到这里的童鞋快奖励自己一口辣条吧!

    想要看CoorChice更多的文章,可以加个关注哦!

    相关文章

      网友评论

      • pre_elvis:从client端发起请求,到server端处理请求,再到client拿到执行结果,这样一个完整的过程,是不是有两次copy啊?
      • 96a614d2cec3:为何如此牛逼!!!!膜拜啊!我刚刚大学毕业出来工作,请问android framework 怎么掌控呀!
        CoorChice:@luwinkey 多看看源码,梳理流程:wink:

      本文标题:Binder机制之一次响应的故事

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