从源码角度看Binder

简介

Binder在Android中堪称武林秘籍中的"易筋经",无论是菜鸟还是老鸟都对之神往。Binder架构是进程间相互通信的最常用手段,四大组件的基本功能都是依赖着Binder才能够实现的。

为了开发者能够使用java与cpp进行binder通信,binder的设计贯穿了framework、native和kernel层,开发者可以轻松的在上层使用binder向其它进程发起数据通信。

这是我第三次系统性的阅读binder代码了,之前的两次可以去 Binder_SourceCodeAndroid 面试考 Binder,看这一篇就够了 中阅读。

这一次的源码分析,除了分析binder数据传输的主干,我会单独提炼出我平时的一些困惑,例如:

  1. binder kernel是如何组织数据结构的,如其中的binder_proc->refs_by_desc, refs_by_node这些重要的红黑树
  2. binder 涉及到的基本并发设计模式,例如典型的生产者-消费者模型的运用
  3. 与binder客户端紧密联系的handle句柄是怎样在kernel中查询到服务进程的
  4. linux 基本数据结构及ioctl, mmap这些基础系统调用的作用及原理
  5. 死亡回调究竟是依赖着什么实现的...

等等问题,都会在这篇文章中一一分析

整体图

设计架构

以上图片大体列出了实名Binder的设计架构,几点需要注意的点:

  1. BinderProxy为客户端在java层的表示,它的成员变量mObject是和native层的表示BpBinder的mObjects一一对应的,通过调用BinderProxy的tractNative方法,java层可以调用到native层BpBinder,随后调用到进行IPC通信的控制器IPCThreadState.transact()向内核发送请求
  2. 客户端持有的mHandle字段是与内核通信的关键,用以查询到对应的binder实体对象与binder服务所在进程。通过mHandler可以查询到binder->ref,再通过binder_ref->binder_node->binder_proc的链路可以拿到binder服务端进程
  3. service_manager本质上其实是维护了一个handle链表,便于客户端通过binder服务name进行查询,随后可以获取到binder代理handle,最终以封装成binder代理对象
  4. 内核除IPC通信以外的核心功能在于,提供了目前查询、命令分发、请求路由等服务
  5. 匿名binder服务与实名binder服务的区别在于,匿名binder服务是直接通过binder调用在进程之间传输的,而实名binder服务需要通过service_manager进行注册、随取随用

数据结构鸟瞰

实体型结构:

  • binder_proc: binder进程
  • binder_thread: binder线程
  • binder_node: binder实体对象
  • binder_ref: binder引用对象
  • binder_work: binder事务,是生产与消费者模式队列中的基本对象
  • binder_transaction: binder调用
  • binder_buffer: binder数据传输空间
  • binder_ref_death: binder服务端死亡通知

数据型结构:

  • binder_write_read: 用户空间与内核空间交互的数据
  • binder_transaction_data: binder_write_read数据的主体
  • flat_binder_objet: binder对象的封装,便于通信传输

Binder设计基础

ioctl(): 内核/用户空间调用

ioctl在linux中的使用很常见,也是binder驱动能够实现用户空间、内核空间之间通信的基础之一。简而言之,它是设备驱动中对设备的I/O通道进行管理的函数,每次调用需要指定三个参数: fd, command与需要传入到内核的数据。其中cmd至关重要,用户将针对设备的命令操作进行分流,素有没有在binder驱动中的ioctl实现中,看到的是常常switch-case条件判断语句

与binder内核通信的ioctl传入的fd是在IPCThreadState初始化时,通过调用open打开/dev/binder文件后获取到的fd,该打开操作只会进行一次,随后的binder调用本质上都是通过ioctl向该fd所在设备进行通信的

frameworks/native/libs/binder/IPCThreadState.cpp

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mMyThreadId(gettid()),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    ...
}

frameworks/native/libs/binder/ProcessState.cpp

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
    ...
    return fd;
}

这里需要的open方法的调用也是需要重点关注下,有了打开也就有了关闭,作为以万物皆文件设计理念著称的Linux,当进程死亡后,会关闭所有该进程打开过的文件。binder驱动正是借助于此,当文件fd关闭后,驱动会被回调通知,然后产生死亡回调,通知监听过该回调的进程。

mmap(): 内核/用户空间内存映射

内存映射的作用简而言之是将用户控件的一段内存区域映射到内核空间,映射之后,用户再对这段内存进行的读写操作,都会和内核空间的对应内存区域保持一致,使用mmap进行普通用户用户控件与内核空间的映射,是实现进程间通信的物理基础

这里特别提一下copy_to_usercopy_from_user这两个方法,在binder内核中使用的十分频繁,经典用法:

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    ...
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;
    ...
    // 从用户空间拷贝数据到内核空间
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    ...
    // 从内核空间拷贝数据到用户空间
    copy_to_user(ubuf, &bwr, sizeof(bwr)
}

有了以上的基础,binder驱动遍有了"建筑地基",可以IPC通信过程中传输数据

Binder中的ONEWAY与非ONEWAY调用

frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
...
    if ((flags & TF_ONE_WAY) == 0) {
        ...
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        ...
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

ONE_WAY这个字段的设置,最终提现在transact阶段中,调用waitForeResponse方法是否传入了reply实体参数

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
     uint32_t cmd;
    int32_t err;
    while (1) {
         // 调用talkWithDriver与binder driver进行通信
        if ((err=talkWithDriver()) < NO_ERROR) break;
        ...

        cmd = (uint32_t)mIn.readInt32();
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        ...
        case BR_REPLY:
            ...
            goto finish;

        ...
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }

    return err;
} 

waitForeResponse本质上是一个无限循环,通过talkWithDriver与binder driver进行通信,根据返回的command进行不同的操作

可以看到,如果reply字段实体对象不为空的话,在获取到binder driver相应成功的命令后,就会跳出循环,进行到finish

Binder中的生产者与消费者

Binder驱动模型中,驱动程序担任着生产者的角色,而binder客户端与服务端进程、线程担任着消费者的角色。实现具体中,消费队列体现为在binder_proc与binder_thread的todo中,消费者从这两个队列中获取需要处理的事务,binder驱动中的事务有体现为binder_work的实例:

角色 职责 binder驱动体现
生产者 生产事务 驱动
消费者 消费事务 binder进程、线程
事务队列 用于存储生产事务,方便消费者取出 binder_proc->todo, binder_thread->todo
事务 用于处理的基本单位 binder_work

binder_work的事务消费都是在binder_thread_read中进行的,以下对binder_work类别进行总结:

Type 场景 涉及队列
BINDER_WORK_TRANSACTION binder驱动对服务端进程进行请求 thread->todo, proc->todo
BINDER_WORK_RETURN_ERROR binder驱动对客户端进程发出错误回复 thread->todo
BINDER_WORK_TRANSACTION_COMPLETE binder驱动对客户端进程的首次回应 thread->todo
BINDER_WORK_NODE binder驱动对实体引用相关操作 thread->todo, proc->todo
BINDER_WORK_DEAD_BINDER binder实体死亡相关操作 thread->todo, proc->todo

这里Binder客户端与服务端虽然都是消费者,但是客户端作为调用者,发起调用的只是普通进程,获取到Binder内核事务队列的机会只是一次性的,而作为服务端,却是在循环的获取事务队列中的数据,并分配空闲的Binder线程进行处理。这里重点关注一下服务端的循环消费。

我们知道,在服务端注册服务时,有两个方法是十分重要的,一个是 ServiceManager.publishService,另外一个是IPCThreadState.joinThreadPool方法,这里joinThreadPool方法的主要功能就是循环的去获取内核的binder_work处理反馈:

frameworks/native/libs/binder/IPCThreadState.cpp

void IPCThreadState::joinThreadPool(bool isMain)
{
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    set_sched_policy(mMyThreadId, SP_FOREGROUND);

    status_t result;
    // 在这里进入循环,循环处理该进程服务的请求
    do {
        processPendingDerefs();
        result = getAndExecuteCommand();
    } while (result != -ECONNREFUSED && result != -EBADF);

    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;

    // 请求driver,查看是否有需要处理的事务
    result = talkWithDriver();
    if (result >= NO_ERROR) {
        ...
        result = executeCommand(cmd);
    }

    return result;
}

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    ...
    // talkWithDriver的核心在于调用ioctl与内核进行通信
    // 最终会在内核中调用binder_thread_read方法
    // 如果没有获取到需要处理的事务,线程会处于阻塞状态
    if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
        err = NO_ERROR;
    else
        err = -errno;
    ...
    // 通信返回后,说明获取到了需要处理的事务,然后会bwr进行处理
}

梳理一下思路:

  • 内核在生产者消费者之间充当了调和的角色,相当于一个controller,用来协调两者之间的工作
  • 生产与消费的场景实质上都发生在内核,服务端线程获取到消费事务后才在自己用户空间中进行处理操作
  • 进程想要发布服务除了使用ServiceManager.addService外,务必要调用joinThreadPool,否则无法处理其它进程的请求事务
  • 每个调用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)的线程,都会在调用的过程中涉及binder_thread_writebinder_thread_read这两个方法,其中前者主要做生产者的操作,后者主要做消费者的操作。binder_thread_read主要使用到了proc->todo与thread->todo这两个队列,当这两个队列不为空时才会唤起线程,否则会产生阻塞

值得注意的时候,binder的设计很大程度上依赖着command的顺序,也就意味着生产与消费是一一对应的,否则的话会导致消费顺序错乱的异常,引导意想不到的问题,例如ONE_WAY的binder调用居然也会卡死

早起的binder版本,为了保持数据的一致性,使用了global lock的方式,很大程度上限制了binder内核的并发性。最新的binder驱动中,通过引入三把锁,分离的早期的global lock,大大提高了性能

题外话,我们可能都比较熟悉经典的生产者与消费者在JAVA的实现,核心思路就是如果事务队列为空,那么消费者则调用wait()进入睡眠状态,不占用进程时间片资源,而生产者在插入事务到队列后会调用notify()来唤起消费者。

在binder内核中,典型的消费者休眠逻辑:

msm-3.18/drivers/staging/android/binder.c

static int binder_wait_for_work(struct binder_thread *thread,
                bool do_proc_work)
{
    ...
    for (;;) {
        // 调用该方法使进程在等待队列上睡眠
        prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
        // 当进程被唤醒后,检查todo队列是否不为空,如果是,则唤醒进程
        if (binder_has_work_ilocked(thread, do_proc_work))
            break;
        ...
        // 否则的话,将该线程的调度权交给调度器,再次进入睡眠
        schedule();
        ..
    }
    ...
}

典型的生产者逻辑:

...
// 将事务插入到todo队列中
binder_enqueue_work_ilocked(&t->work, &target_thread->todo);
...
// 随后唤醒等待队列
wake_up_interruptible_sync(&target_thread->wait);
...

Binder代理对象的handle句柄

大家在谈及binder的设计基础时,往往说的最多的还是内存映射。的确,没有内存映射的基础设施,使用binder进行IPC通信就是天方夜谭,然而当我们从程序设计的角度看待这个问题时,就能发现binder内核与binder客户端常用到的代理对象handle句柄却是贯穿着IPC通信的核心所在。

binder代理对象在客户端的抽象BinderProxy,它通信的核心其实就在于调用transactNative方法,通过JNI调用到native层的binder代理对象BpBinder来实现与内核的通信。BinderProxy从设计的层次来看只是一个通过JNI调用到native的封装。

frameworks/native/libs/binder/BpBinder.cpp

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        // 与内核通信
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

这里BpBinder实际上也是委托IPCThreadState来与内核进行通信的,调用transact方法的关键是传入mHandle作为Binder服务的标志,类似与HashMap中的键,以帮助内核查找到对应的实体对象

这里的mHandle是在BpBinder创建时传入的,而无论是匿名Binder还是实名Binder服务,对应的BpBinder的创建操作是在跨进程传输时,通过调用Parcel.readStrongBinder进行的

这里深入到IPCThreadState->transact方法中:

frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    ...
    if (err == NO_ERROR) {
        // 将handle写入到binder_transaction_data中
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    ...
    if ((flags & TF_ONE_WAY) == 0) {
        ...
        // 最终还是通过调用waitForResponse来实现通信
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        ...
    } else {
        err = waitForResponse(NULL, NULL);
    }
    ...
}

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
    tr.target.handle = handle;
    ...
    mOut.writeInt32(cmd);
    // 特殊留意一下,binder_transaction_data最终被包装到Parcel类型的mOut中
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        ...
    }
}

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    binder_write_read bwr;
    ...
    bwr.write_size = outAvail;
    // 获取Parcel的数据,封装到binder_write_read中
    bwr.write_buffer = (uintptr_t)mOut.data();
    ...
    // 最终将binder_write_read发送到内核,其中handle数据就在其中
    if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
        err = NO_ERROR;
    else
        err = -errno;
    ...
}

handle在内核中的表现名称为desc,是binder_ref的成员,当binder请求调用到驱动时,典型的获取实体对象代码如下:

msm-3.18/drivers/staging/android/binder.c

static void binder_transaction(struct binder_proc *proc,
                               struct binder_thread *thread,
                               struct binder_transaction_data *tr, int reply,
                               binder_size_t extra_buffers_size)
{
    ...
    // 通过使用binder_proc->refs_by_desc红黑树与目前binder代理对比,获取都对应的binder_ref
    ref = binder_get_ref_olocked(proc, tr->target.handle,
                                 true);
    if (ref) {
        // 随后获取到binder_ref对应的binder_node,核心获取操作是调用ref->node
        target_node = binder_get_node_refs_for_txn(
            ref->node, &target_proc,
            &return_error);
    }
    ...
}

static struct binder_node *binder_get_node_refs_for_txn(
        struct binder_node *node,
        struct binder_proc **procp,
        uint32_t *error)
{
    struct binder_node *target_node = NULL;

    binder_node_inner_lock(node);
    if (node->proc) {
        target_node = node;
        ...
        // 通过node获取target_proc
        *procp = node->proc;
    } else
        *error = BR_DEAD_REPLY;
    binder_node_inner_unlock(node);

    return target_node;
}

通过以上代码,我们可以大致分析出发送binder调用时,在发送线程通过IPCThreadState传入binder代理的handle到内核后,会通过记录在binder_proc的refs_by_desc红黑树获取到binder_ref,随后再通过binder_ref->binder_node->binder_proc的调用链获取到实体对象运行在的服务端进程。随后可以将binder_work入队到binder_proc或binder_thread的todo队列中,完成剩余的操作

当binder调用发送完毕后,如果该调用是非ONE_WAY调用,那么在服务进程处理完成后,如果调用回之前的客户端进程,返回处理后的信息呢?

msm-3.18/drivers/staging/android/binder.c

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply,
                   binder_size_t extra_buffers_size)
{
    ...
    // 获取发起调用时,被保存的stack信息
    in_reply_to = thread->transaction_stack;

    // 从stack中获取发起调用的线程,也就是返回操作的目标线程
    target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
    ...
}

从上面的代码我们可以看出,当服务端处理完毕后,会返回信息调用到binder内核,之所以返回的调用不需要指定handle,是因为使用了被记录的from信息,可以轻松获取到返回目标线程

Binder内核中的红黑树

binder驱动中创建了很多方便索引、查询的数据结构,其中最重要的莫过于红黑树。这里总结了具代表性的几个结构,有人常常会发问,为什么binder_ref需要两颗树来索引,其中答案很简单,归根结底还是每颗树使用的场景不同,使用不同的键来查询效率不同罢了

红黑树位置 意义 节点位置 使用场景
binder_proc->refs_by_desc 进程中所有使用的代理引用,以desc为键 binder_ref->rb_node_node 方便以handle进行查询,多用于发送binder调用
binder_proc->refs_by_node 进程中所有使用的代理引用,以node为键 binder_ref-> 方便以node查询,多用于增加node引用
binder_proc->nodes 进程中所有实体服务节点 binder_node 方便已服务端服务地址查询,多用于binder对象传输
binder_proc->threads 进程中所有binder线程 binder_thread 方便获取binder线程,以PID进行搜索,使用范围很广

辅助功能:实名服务的注册与获取

这里我们以system_server注册AMS服务为示例来分析service_manager是如果完成服务的注册与获取的:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void setSystemProcess() {
    ...
    ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
    ...
}

可以看到,addService必需的前两个参数为服务name与服务的实体对象,其中name会作为service_manager提供查询服务的键值,而实体对象的地址引用会被封装为binder_node保存在binder kernel中

frameworks/base/core/java/android/os/ServiceManager.java

public static void addService(String name, IBinder service) {
    try {
        getIServiceManager().addService(name, service, false);
    } catch (RemoteException e) {
        Log.e(TAG, "error in addService", e);
    }
}

实名服务是通过service_manager进行查询的,那么service_manager的引用该从哪里获取的,这其实是一个先有鸡还是先有蛋的问题,binder的设计者给出了答案,是先有了"鸡"才有了"蛋"。

通过使用固定不变的handle值0来表示service_manager的引用句柄,以此就可以直接与binder内核通信,查询并获取到service_manager的引用对象,直接调用addService,这里的代码就不细分析了,后面getService的流程与此类似

frameworks/native/libs/binder/IServiceManager.cpp

virtual status_t addService(const String16& name, const sp<IBinder>& service,
                            bool allowIsolated)
{
    Parcel data, reply;
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    data.writeInt32(allowIsolated ? 1 : 0);
    // 获取BpBinder对象
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

通过调用remote()方法获取到BpBinder,时候调用transact方法以发起类型为ADD_SERVICE_TRANSACTION的binder调用

frameworks/native/libs/binder/BpBinder.cpp

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
        // 还是通过IPCThreadState进行调用,这里的mHandle句柄为0
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

往下iPCThreadState是如何发起调用到内核,内核是如何查找到service_manager服务进程并将进程唤醒、进行服务方法调用的,这些细节就不再罗列了,之前的ioctl介绍、生产者消费者模型等基础知识已经解释过了。这里直奔service_manager进程本身:

frameworks/native/cmds/servicemanager/service_manager.c

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    uint16_t *s;
    ...
    switch(txn->code) {
        case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        // 进行服务添加
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;
    }
    ...
}

svcmgr_handler函数是在service_manager进程启动后,在循环体中不断被调用到的服务函数。SVC_MGR_ADD_SERVICE为服务添加的对应code,最终会在svclist中插入对应的svcinfo子项

frameworks/native/cmds/servicemanager/service_manager.c

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    uint16_t *s;
    ...
    switch(txn->code) {
        case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        // 进行服务查询
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;
    }
    ...
}

getService的调用流程和addService的调用流程大致类似,主要的区别有两点:

  1. 调用的进程不同,addService在服务端进行调用,而getService在客户端中进行调用
  2. 在service_manager中的执行命令不同,前者会在列表中加入子项,后者查询列表中的子项

核心功能:跨进程数据传输

这里以AMS.getRecentTasks方法为实例分析binder跨进程数据传输,这个方法常常被SystemUI用于最近任务卡片的获取,它的调用需要向AMS传递三个基本数据参数,AMS处理完成后回返回一个列表。因为调用是非ONE_WAY的,所以该数据传输是双工的

frameworks/base/core/java/android/app/ActivityManagerNative.java

public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeInt(maxNum);
    data.writeInt(flags);
    data.writeInt(userId);
    // 这里的mRemote在其实是BinderProxy的实例,通过transact调用与binder内核进行通信
    mRemote.transact(GET_RECENT_TASKS_TRANSACTION, data, reply, 0);
    reply.readException();
    final ParceledListSlice<ActivityManager.RecentTaskInfo> list = ParceledListSlice.CREATOR
        .createFromParcel(reply);
    data.recycle();
    reply.recycle();
    return list;
}

这里的调用流程和上述类似,大概就是BinderProxy->BpBinder->IPCThreadState,这里特别留意一下传输数据的封装,直接看IPCThreadState的相关代码:

frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    ...
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        // 将数据进行封装
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    ...

    if ((flags & TF_ONE_WAY) == 0) {
        // 非ONE_WAY通信,传入reply参数,意味着服务端回返回数据
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    }
    ...
}

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
    ...
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    }
    ...
    // 将Parcel数据封装成binder_transaction_data,其中包装了命令与数据内容
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

注意一下,需要发送给binder驱动的数据,此时已经被包装到mOut成员变量中

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        ...
    }
    ...
}

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    binder_write_read bwr;
    ...
    bwr.write_size = outAvail;
    // 获取mOut中的数据
    bwr.write_buffer = (uintptr_t)mOut.data();
    ...
    // 调用ioctl和内核通信,通信数据为binder_write_read
    if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
    ...
}

先调用talkWithDriver与内核通信

msm-3.18/drivers/staging/android/binder.c

```c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
// proc引用保存在file的private_data引用中
struct binder_proc *proc = filp->private_data;
...
switch (cmd) {
// command为BINDER_WRITE_READ,进入该代码块
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
...
}

static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
struct binder_write_read bwr;
// 从用户空间中拷贝数据到内核空间
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
...
if (bwr.write_size > 0) {
// write_size大于0时,调用该方法进行事务生产
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,

top Created with Sketch.