Binder机制之一次响应的故事

阅读 196
收藏 20
2017-08-24
原文链接:www.jianshu.com
image
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
image

好了,接着上次来!

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

image
image

好吧,再放个高清的下载地址:ogemdlrap.bkt.clouddn.com/Binder%E8%B…

通讯发起端的休眠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
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
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
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
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
image

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

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

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

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

评论