Android技术知识Android开发Android精选

Binder 驱动详解(下)

2019-01-31  本文已影响37人  SharryChoo

前言

通过 Binder 上一篇文章的分析, 我们知道了 Binder 驱动在我们应用开发过程中的使用方式, 了解到了 BBinderBpBinder 两个非常重要的 Native 对象, 本次我们就着重分析一下 Binder 在运行时库层知识

因为运行时库是使用 C/C++ 编写的, 对于 Android 开发者来说, 可能有些晦涩难懂, 笔者也下了很大的功夫, 对此感兴趣的同学可以耐心往下读, 如果存在让大家兴奋的点, 那么笔者就十分满足了

一. AndroidRuntime 层的 Binder 库

封装的意义

Android 系统将各种 Binder 驱动程序操作封装成一个 Binder 库, 进程就可以使用 Binder 库, 方便地调用 HAL 驱动提供的服务, 屏蔽了底层的细节, 更有利于开发者进行使用

Binder 库关键类

在 Binder 库中, Service 组件与 Client 组件分别使用模板类 BnInterfaceBpInterface 来描述

1. BnInterface

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder 
{
public:
    virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
    virtual cosnt String16& getInterfaceDescriptor() const;
    
protected:
    virtual IBinder* onAsBinder();
}

接下来分析一下 BBinder 的实现

class BBinder : public IBinder
{
public:
    ......
    virtual status_t transact(
        unit32_t code,
        const Parcel& data,
        Parcel* reply,
        unit32_t flag = 0
    );
proctected:
    ......
    virtual status_t onTransact(
        unit32_t code,
        const Parcel& data,
        Parcel* reply,
        unit32_t flag = 0
    )
}

BBinder 类有两个重要的成员函数 transact 和 onTransact

class IBinder : public RefBase {
    ......
}

可见 IBinder 类又继承了 RefBase, 也就是说 Binder 本地对象是通过引用计数技术来维护生命周期的

2. BpInterface

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase 
{
public:
    BpInterface(const sp<IBinder>& remote);
    
protected:
    virtual IBinder* onAsBinder();
}

模板类 BpInterface 继承了 BpRefBase, 后者为 Binder 代理对象提供了抽象的进程间通信接口

class BpRefBase : public virtual RefBase 
{
protected:
    BpRefBase(const sp<IBinder>& o);
    ......
    inline IBinder* remote() {
        return mRemote;
    }
    inline IBinder* remote() const {
        return mRemote;
    }
private:
    .......
    IBinder* const mRemote;
}

可以看到 BpRefBase 中有一个成员变量 mRemote, 它的实现类为 BpBinder, 看看这个实现类是做了哪些操作

class BpBinder : public IBinder 
{
public: 
    BpBinder(int32_t handle);
    inline int32_t handle() const {
        return mHandle;
    }
    ......
    virtual status_t transact(
        unit32_t code,
        const Parcel& data,
        Parcel* reply,
        unit32_t flags = 0
    );
    ......
private:
    const int32_t mHandle;
}

可以看到 BpBinder 中有一个 mHandle 句柄, 它表示 Binder 引用对象的句柄值, 可以通过 handle 来获取

3. IPCThreadState

class IPCThreadState 
{
public:
    static IPCThread* self();
    ......
    status_t transact(
        int32_t handle,
        unit32_t code, 
        const Parcel& data,
        Parcel* reply,
        unit32_t flags
    );
    ......
private:
    status_t talkWithDriver(bool doRecive = true);
    ......
    const sp<ProcessState> mProcess;
    ......
}

可以看到 IPCThreadState 内部存在一个成员变量 mProcess

接下来看看 ProcessState 的实现

4. ProcessState

class ProcessState: public virtual RefBase 
{
public:
    static sp<ProcessState> self();
    ......
private:
    int mDriverID;
    void* mVMStart;
}

应用层 Binder 库的 UML 图

好的至此, 我们队 Binder 库中几个非常重要的 C++ 对象有了一定的认识, 接下来看看这几个类的相互依赖关系


运行时库依赖图.png

二. ServiceManager 的启动

运行时库中的 ServiceManager 与 Java 中的 ServiceManager 是对应的

由于篇幅原因, 这里就不介绍 Java 中的 ServiceManager 了, 感兴趣的同学可以看看 Zygote 与系统服务进程的启动, 没准可以找到你想要的答案

启动入口

ServiceManager 该程序的入口函数 main 实现在 service_manager.c 中

// frameworks/base/cmds/servicemanager/service_manager.c
int main(int argc, char **argv) {
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
    // 打开设备文件
    bs = binder_open(128*1024);
    // 将自己注册为 Binder 驱动的上下文管理者
    if (binder_become_context_manager(bs)) {
        return -1;
    }
    svcmgr_handle = svcmgr;
    // 循环等待和处理 Client 进程的通信请求
    binder_loop(bs, svcmgr_handler);
    return 0;
}

可见 service_manager 的主函数中主要做了三件事情

  1. 调用 binder_open 打开 binder 设备文件 /dev/binder, 并且将其映射到本进程的地址空间, 返回一个 binder_state 结构体
  2. 调用 binder_become_context_manager 将自己注册成为一个 Binder 进程间通信的上下文管理者
  3. 调用函数 binder_loop 来循环等待和处理 Client 进程的通信请求

打开映射 Binder 设备文件

// frameworks/base/cmds/servicemanager/binder.c

struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    // 在堆内存中创建了 binder_state 的实例
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }
    // 调用 open 函数打开 Binder 设备文件
    bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        goto fail_open;
    }
    // 将给进程分配的内核缓冲区大小记录到 binder_state 结构体对象中
    bs->mapsize = mapsize;
    // 调用函数 mmap 将设备文件 /dev/binder 映射到地址空间, 并且将其地址空间的首地址记录到 binder_state 结构体对象中
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
    // 返回这个 binder_state 这个结构体对象
    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

可见 ServiceManager 的打开设备文件的操作非常简单

注册为 Binder 的上下文管理者

// frameworks/base/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

可以看到注册上下文管理者的函数中, 调用了 ioctl 这个函数(即 Binder IO controller, 用于用户空间与 Binder 驱动交互)

接下来简单的看一下, Binder 内核驱动中对这个 IO 控制命令做了哪些处理

// Binder 通信上下文管理者的在 Binder 内核驱动中的 Binder 实体对象
static struct binder_node *binder_context_mgr_node;
// 描述了注册了 Binder 通信上下文管理者的有效用户 ID
static struct binder_context_mgr_uid = -1;

static long binder_ioctrl(struct file *filp, unsigned int cmd, unsigned long arg) {
    // 获取当前进程的 binder 线程, 没有则创建一个
    thread = binder_get_thread(proc);
    
    switch(cmd) {
        ......
        case BINDER_SET_CONTEXT_MGR:
            // 说明 Binder 上下文管理者已经注册过了
            if (binder_context_mgr_node != NULL) {
                goto error;
            }
            // 说明 Binder 上下文管理者已经注册过了
            if (binder_context_mgr_uid != -1) {
                goto error;
            } else {
                // 经过一系列验证之后, 给当前进程创建其对应的 binder 实体对象保存在全局的 binder_context_mgr_node 变量中
                binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
            }
            ......
            break;
    } 
}

Binder 内核驱动中针对 BINDER_SET_CONTEXT_MGR 这个控制码, 主要做了以下操作

循环等待处理 Client 进程间的请求

// frameworks/base/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    // BC_ENTER_LOOPER: 控制位的含义是, 将当前线程注册成为 Binder 线程
    // 以便 Binder 驱动程序可以将进程间的通信请求分发给它处理
    readbuf[0] = BC_ENTER_LOOPER;
    // 该函数通过 IO 控制命令将 readbuf 发送给 Binder 驱动程序, 通知其处理 readbuf 中的控制位
    binder_write(bs, readbuf, sizeof(uint32_t));
    // for 循环从 binder 驱动中获取需要处理的进程间通信请求
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        // 通过 BINDER_WRITE_READ 控制位, 从 Binder 驱动中获取当前是否有新的进程间请求需要处理
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        // 处理进程间的请求
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

binder_loop 主要做了以下几件事情

binder 驱动注册 looper 线程

接下来看看 binder_write 方法的实现

// frameworks/base/cmds/servicemanager/binder.c
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;
    // 将数据存储在 write_buffer 中, 即 BC_ENTER_LOOPER 这个控制码
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    // 调用 ioctl 与 binder 驱动通信, 请求码为 BINDER_WRITE_READ
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

可见真正用于和 Binder 内核驱动交互的请求码为 BINDER_WRITE_READ, 接下来看看 binder 驱动做了哪些处理

static long binder_ioctrl(struct file *filp, unsigned int cmd, unsigned long arg) {
    // 获取当前进程的 binder 线程, 没有则创建一个
    thread = binder_get_thread(proc);
    
    switch(cmd) {
        ......
        case BINDER_WRITE_READ:
            ......
            if (bwr.write_size > 0) {
                // 可见这里将 BC_ENTER_LOOPER 请求码转发给了 binder_thread_write 函数
                ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.read_consumed);
            }
            ......
            break;
    } 
}

int binder_thread_write(......) {
    while(...) {
        switch(cmd) {
            case BC_ENTER_LOOPER:
                // 这里将这个线程注册成为了 looper 线程, 至此 Binder 进行间的通信请求便会交由这个线程处理
                thread->looper |= BINDER_LOOPER_STATE_ENTERED;
            break;
        }
    }
}

至此, ServiceManager 的主线程便可以接收到 Binder 驱动发送的通信请求了

了解了 ServiceManager 如何启动了之后, 我们就进入重头戏, 看看一次 Binder 驱动通信的流程是如何进行的

三. ServiceManger 进程间通信

Client 端通信的发起

defaultServiceManager()->addService(String16("SampleService"), new SampleService);

在应用框架层中, 获取 ServiceManager 代理对象的方式为 defaultServiceManager()

接下来看看 addService 这个方法在 BpServiceManager 代理类中的实现

    // frameworks/base/libs/binder/IServiceManager.cpp
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        // 1. 将通信参数封装到 data 中
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        // 2. 调用 BpBinder 的 transact
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        // 3. 读取请求结束后 Server 通过 Binder 驱动返回回来的数据
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

代理实现方法主要做了以下几步操作

BpBinder 的 transact 操作

// frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // mAlive 用于判断 Binder 代理对象所引用的 Binder 本地对象是否存活
    if (mAlive) {
        // 调用了 IPCThreadState 的 transact 方法
        // mHandle 为这个代理对象的句柄值
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    // 进行错误检查
    status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        // 1. 将 data 封装到一个 binder_transaction_data 结构体对象中
        // handle 为当前 Binder 代理对象的句柄值
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    // TF_ONE_WAY 若为 0 则说明是同步的进程间请求
    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            // 2. 通过 waitForResponse 向 Binder 驱动发送上面封装的 binder_transaction_data 结构体对象
            // 操作码为 BC_TRANSACTION
            err = waitForResponse(reply);
        } else {
            ......
        }
    } else {
        ......
    }
    return err;
}

可见 Client 调用远程方法时, 其代理对象的 transact 方法主要做了以下的操作

先看看如何将 data 等数据封装成为 binder_transaction_data

// frameworks/native/libs/binder/IPCThreadState.cpp
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 对象
    binder_transaction_data tr;
    // 赋初始值
    tr.target.ptr = 0; 
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    // 错误检查
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        // 将 data 中的数据拷贝到 tr 中
        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();
    } else if (statusBuffer) {
        ......
    } else {
        ......
    }
    // mOut 描述一个命令缓冲协议区
    mOut.writeInt32(cmd);// 将 cmd 这个命令写入, 表示这个命令之后需要发送给 Binder 驱动
    mOut.write(&tr, sizeof(tr));// 将 tr  这个结构体写入, 用于后续与 Binder 驱动交互

    return NO_ERROR;
}

writeTransactionData 这个函数做的事情与我们上述一致

接下来看看 waitForResponse 如何通过 tr 和 BC_TRANSACTION 命令与 Binder 驱动程序进行交互

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

    while (1) {
        // 可见这个里调用了 talkWithDriver() 与 Binder 驱动交互
        if ((err=talkWithDriver()) < NO_ERROR) break;
        if (err < NO_ERROR) break;
        // 缓冲区 mIn 这个与 mOut 相对应, 它用于保存从 Binder 驱动程序接收到的返回协议
        if (mIn.dataAvail() == 0) continue;
        cmd = (uint32_t)mIn.readInt32();
        ......
    }
}

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    ......
    // 1. 定义 binder_write_read 结构体, 指定输入缓冲区和输出缓冲区
    binder_write_read bwr;
    
    // 指定从当前进程输出到 Binder 驱动的缓冲区
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();// 将要输出的数据保存到 bwr 的 write_buffer 变量中
    // doReceive 用来描述调用者是否可以收到 Binder 的返回协议码
    if (doReceive && needRead) {
        // 设置从 Binder 驱动输入到当前进程缓冲区的相关参数
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    
    // 如果输出缓冲区和输入缓冲区大小都为 0, 说明不需要与 Binder 驱动交互
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        ......
        // 2. 使用 IO 控制命令 BINDER_WRITE_READ 来与 Binder 驱动进行交互, 说明要进行读写操作
        // bwr 即为读写操作的内容
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        ......
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        // 将 Binder 驱动已处理的命令协议从 mOut 中移除
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        // 将 Binder 驱动返回的命令协议保存到 mIn 中
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
    return err;
}

talkWithDriver 这个函数非常重要, 它是 Binder 停留在应用程序框架层的最后一个函数, 主要做了如下操作

Binder 驱动处理 BC_TRANSACTION 协议码

// kernel/goldfish/drivers/staging/android/binder.c
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)
{
    uint32_t cmd;
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    while (ptr < end && thread->return_error == BR_OK) {
        // 获取从用户空间传递过来的指令码保存在 cmd 中, 由上面可知, cmd 为 BC_TRANSACTION
        if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        ......
        switch(cmd) {
        ......
        // 处理 BC_TRANSACTION/BC_REPLY
        case BC_TRANSACTION:
        case BC_REPLY: {
            // 从用户空间拷贝数据到 transaction_data 中
            struct binder_transaction_data tr;
            if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            // 进行指令码的处理操作
            binder_transaction(proc, thread, &tr,
                       cmd == BC_REPLY, 0);
            break;
        }
        ......
        }
}

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)
{
    ......
    if (reply) {// 处理 BC_REPLY 指令, 到后面分析
       ....
    } else {
       // 处理 BC_TRANSACTION 指令
       if (tr->target.handle) {
            // 1. 获取 Client 调用的 binder 引用对象
            struct binder_ref *ref;
            // 从 client 进程中, 通过句柄值, 获取其在 linux 内核驱动的引用对象
            ref = binder_get_ref(proc, tr->target.handle, true);
            // 2. 通过引用对象找到其对应的实体对象
            target_node = ref->node;
        } else {
            target_node = context->binder_context_mgr_node;
        }
        ......
        // 3. 通过 binder 实体对象, 找对对应的 Server 进程
        target_proc = target_node->proc;
        ......
        // 4. 尝试在 Server 进程找到最合适的空闲线程去处理这次 Client 端的请求
        if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
            struct binder_transaction *tmp;
            tmp = thread->transaction_stack;
            ......
            while (tmp) {
                if (tmp->from && tmp->from->proc == target_proc)
                    target_thread = tmp->from;
                tmp = tmp->from_parent;
            }
        }
    }
    // 5. 将目标线程的 todo 队列和 wait 队列保存到成员变量中
    if (target_thread) {
        // 更新成员变量指向目标线程中的相关属性
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    } else {
        // 更新成员变量指向目标进程中的相关属性
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    };

    /* TODO: reuse incoming transaction for reply */
    // 6.1 binder_transaction 对象 t 会被封装成为 BINDER_WORK_TRANSACTION 工作项, 
    // 后续会添加到 Server 目标线程的 todo 中, 以便其能够接受到 Binder 驱动发送的 BR_TRANSACTION 协议
    t = kzalloc_preempt_disabled(sizeof(*t));
    // 6.2 binder_transaction 对象 tcomplete 会被封装成 BINDER_WORK_TRANSACTION_COMPLETE 工作项
    // 后续会发送到 Client 发起线程的 todo 队列中, 以便其能够接收到 Binder 驱动发送的 BR_TRANSACTION_COMPLETE 协议
    tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete));
    // 初始化 t
    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        t->from = NULL;
    t->sender_euid = task_euid(proc->tsk);
    t->to_proc = target_proc;
    t->to_thread = target_thread;
    t->code = tr->code;
    t->flags = tr->flags;
    t->priority = task_nice(current);
    // 从 tr 中复制数据到目标进程的内核缓冲区
    t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, extra_buffers_size, !reply && (t->flags & TF_ONE_WAY));
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t;
    t->buffer->target_node = target_node;
    ......
}

好了, 总结一下, 这个 binder 内核中处理用户空间指令码交换的方法主要是 binder_transaction, 关于 BC_TRANSACTION 它主要做了如下操作

接下来就要处理方法的调用了, 我们在应用层调用了 data.writeStrongBinder(binder); 将 binder 注册到目标进程中, 看看 binder_transaction 是怎样处理的

    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        fp = (struct flat_binder_object *) (t->buffer->data + *offp)
        switch (fp->type) {
        // 我们开始的时候调用的是 put 方法
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
              struct binder_ref *ref;
              // 8. 尝试从 Client 进程中获取参数中 binder 对象在 linux 内核驱动中的实体对象
              struct binder_node* node = binder_get_node(proc, fp->binder);
              if (node == NULL) {
                  // 若源进程没有其实体对象, 则调用 binder_new_node 创建一个
                  node = binder_new_node(proc, fp->binder, fp->cookie);
              }
              // 9. 获取 Sever 进程需要使用的 binder 引用对象(没有则创建一个)
              ref = binder_get_ref_for_node(target_proc, node);
        } break;
    }

可见这些的操作非常的重要

好的, 线程参数数据也已经获取完了, 我们接着往下看 binder_transaction

    if (reply) {
        ......
    } else if (!(t->flags & TF_ONE_WAY)) {
        // 同步操作
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;
    } else {
        // 若为异步操作, 则将任务添加到目标 binder 进程的异步队列中
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
            target_node->has_async_transaction = 1;
    }
    // 将 t 的工作项设置为 BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    // 添加到目标进程的工作队列的尾部
    list_add_tail(&t->work.entry, target_list);
    // 将 tcomplete 的工作项设置为 BINDER_WORK_TRANSACTION_COMPLETE 
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    // 添加到源进程的工作项的尾部
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait) {
        // 唤醒目标线线程去执行 BINDER_WORK_TRANSACTION 任务
        wake_up_interruptible(target_wait);
    }
    return;

找到了目标进程以及需要执行的线程后, 源线程和目标进程就回去并发的处理自己的工作项了

Client 端处理 BINDER_WORK_TRANSACTION_COMPLETE 工作项

static int binder_thread_read(.......) {
    ......
    while(1) {
        switch(w->type) {
        ......
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            // 将 BR_TRANSACTION_COMPLETE 返回用户空间
            cmd = BR_TRANSACTION_COMPLETE;
            if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            binder_stat_br(proc, thread, cmd);
            list_del(&w->entry);
            kfree(w);
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
        } break;
        ......
        }
    }
    
}

可见 binder_thread_read 对 BINDER_WORK_TRANSACTION_COMPLETE 处理也非常简单

Client 端用户空间处理 BR_TRANSACTION_COMPLETE 协议码

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        // 从输入缓冲区中读取, 是否有 Binder 驱动写入的数据
        cmd = (uint32_t)mIn.readInt32();
        // 主要查看 BR_TRANSACTION_COMPLETE 指令码
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        ......
    }

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

可以看到 BR_TRANSACTION_COMPLETE 指令码很简单

所以, 接下来的重头戏便是我们需要查看目标线程对 Binder 驱动发出的 BINDER_WORK_TRANSACTION 指令的处理

Server 端处理 BINDER_WORK_TRANSACTION 工作项

由前面可知, BINDER_WORK_TRANSACTION 会将工作项添加到目标进程的 todo 队列中, 那么目标进程就会被唤醒, 进而执行器 binder_thread_read 处理 todo 队列中的工作项

// kernel/goldfish/drivers/staging/android/binder.c
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)
{ 
    ......
    // 循环从其读取器工作项数据
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        // 1. 从其线程/进程的 todo 队列中获取工作项, 并且将数据存入 binder_work 结构体对象中
        if (!list_empty(&thread->todo)) {
            w = list_first_entry(&thread->todo, struct binder_work,
                         entry);
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            w = list_first_entry(&proc->todo, struct binder_work,
                         entry);
        } else {
            ......
            break;
        }
        // 2. 处理工作项中对应的指令码
        switch (w->type) {
        // 我们主要关注对 BINDER_WORK_TRANSACTION 的处理
        case BINDER_WORK_TRANSACTION: {
            // 2.1 将 binder_work 转为一个 binder_transaction 结构体对象
            t = container_of(w, struct binder_transaction, work);
        } break;
        ......
        // 3. 将 binder_transaction 中的数据从 binder_transaction_data 中, 以便后续可以传输到用户空间
        if (t->buffer->target_node) {// target_node 不为 NULL, 则指定协议码为 BR_TRANSACTION
            struct binder_node *target_node = t->buffer->target_node;
            // 将目标线程 binder 本地对象的信息复制到 tr 中, 以便目标线程的 thread 接收到 binder 驱动发送的 BR_TRANSACTION 之后, 可以将返回协议交给指定的 binder 本地对象处理
            tr.target.ptr = target_node->ptr;
            tr.cookie =  target_node->cookie;
            t->saved_priority = task_nice(current);
            // 保证目标线程的优先级 < 源线程的优先级
            if (t->priority < target_node->min_priority &&
                !(t->flags & TF_ONE_WAY))
                binder_set_nice(t->priority);
            else if (!(t->flags & TF_ONE_WAY) ||
                 t->saved_priority > target_node->min_priority)
                binder_set_nice(target_node->min_priority);
            cmd = BR_TRANSACTION;
        } else {
            ......
        }
        .......
        // 4. 将 tr 数据拷贝到目标进程的用户空间中
        if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        // 将对应的协议码拷贝到用户空间中
        if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);
        .......
        // 5. 这个工作项已经被处理了, 从链表中删除
        list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        // 判断是否为同步请求
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            // 压入目标线程的任务栈中
            thread->transaction_stack = t;
        } else {
            t->buffer->transaction = NULL;
            kfree(t);
        }
        break;
    }
    return 0;
}

可见目标线程被唤醒之后他在 binder 驱动中做了如下的事情

好的, 接下来就进入了目标进程的用户空间了

Server 端处理 BR_TRANSACTION 协议码

ServiceManager 被 Binder 驱动唤醒后, 会调用 binder_parse 方法来处理从 Binder 驱动程序中接收到的返回协议

// frameworks/base/cmds/servicemanager/binder.c
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) {
        // 1. 从用户空间的缓冲区中读取 Binder 驱动传递过来的协议码
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        switch(cmd) {
        ......
        // 这里主要关注 BR_TRANSACTION 协议
        case BR_TRANSACTION: {
            // 2. 从缓冲区中获取通信数据的结构体
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            ......
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;       // 解析从 Binder 驱动程序读取回来的进程间通信数据
                struct binder_io reply;     // 将通信结果写入 reply 中以便于传给 Binder 驱动, 进而返回源进程
                int res;
                // 3. 初始化 reply 和 rdata
                bio_init(&reply, rdata, sizeof(rdata), 4); 
                // 4. 解析 txn 中的数据到 msg 中
                bio_init_from_txn(&msg, txn);
                // 5. 调用 func 函数指针, 处理协议, 将结果写入 reply
                res = func(bs, txn, &msg, &reply);
                ......
                // 将通信结果返回给 binder 驱动
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
        }
        }
    }
}

binder_parse 中所做的事情非常清晰

func 函数指针对本次跨进程调用的处理

在 ServiceManager 中, func 这个函数指针, 指代 svcmgr_handler 这个函数

// frameworks/base/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)
{
    ......
    // 验证接口名称的描述
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }
    // 执行对应的方法
    switch(txn->code) {
    case SVC_MGR_ADD_SERVICE:
        // 获取一个要注册服务的名称 (如"ActivityManagerService")
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        // 从 msg 中取出要注册的服务 binder 引用对象的句柄值
        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;

    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }
    // 调用这个函数, 将成功代码 0 写入到 binder 结构体 reply 中
    bio_put_uint32(reply, 0);
    return 0;
}

可以看到一个非常重要的函数 do_add_service 这个函数真正执行了服务的添加过程

// frameworks/base/cmds/servicemanager/service_manager.c
int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;
    
    if (!handle || (len == 0) || (len > 127))
        return -1;

    // 判断 uid 所指代的源进程, 是否有资格进行注册操作
    if (!svc_can_register(s, len, spid, uid)) {
        return -1;
    }
    // 判断 si 服务是否已经注册了
    si = find_svc(s, len);
    if (si) {
        ......
    } else {
        // 创建一个 svcinfo 并且链入 svclist 中
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;
    }
    binder_acquire(bs, handle);
    // 绑定死亡通知
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

至此就成功的将一个 Service 组件注册到 ServiceManager 中了

binder_send_reply 将通信结果返回给 binder 驱动

// frameworks/base/cmds/servicemanager/service_manager.c
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;
    // cmd_free 的协议码为 BC_FREE_BUFFER
    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    // cmd_reply 的协议为 BC_REPLY
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    // 将一些数据写入 data 的 txn 中
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } 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;
    }
    // 其内部调用 ioctl() 函数和 IO 控制命令 BINDER_WRITE_READ 将 BC_FREE_BUFFER/BC_REPLY 发送给 binder 驱动程序
    binder_write(bs, &data, sizeof(data));
}

可以看到 binder_send_reply 中做的操作也比较清晰

接下来看看 Binder 驱动如何处理这些协议码的

Binder 驱动处理 BC_FREE_BUFFER/BC_REPLY 协议码

BC_FREE_BUFFER 协议码的处理

// kernel/goldfish/drivers/staging/android/binder.c
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)
{
    ......
    while (ptr < end && thread->return_error == BR_OK) {
        // 获取从用户空间传递过来的指令码保存在 cmd 中, 由上面可知, cmd 为 BC_TRANSACTION
        if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        ......
        switch(cmd) {
        ......
        // 处理 BC_FREE_BUFFER 协议码
        case BC_FREE_BUFFER: {
            binder_uintptr_t data_ptr;
            struct binder_buffer *buffer;
            // 从用户空间中得到要释放的内核缓冲区的地址, 存放到 data_ptr 中
            if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);
            // 获取缓冲区
            buffer = binder_buffer_lookup(proc, data_ptr);
            if (buffer == NULL) {
                break;
            }
            // 判断是否允许释放
            if (!buffer->allow_user_free) {
                break;
            }
            // 说明内核缓冲区分配给 transaction 使用的
            if (buffer->transaction) {
                buffer->transaction->buffer = NULL;
                buffer->transaction = NULL;
            }
            if (buffer->async_transaction && buffer->target_node) {
                BUG_ON(!buffer->target_node->has_async_transaction);
                if (list_empty(&buffer->target_node->async_todo))
                    buffer->target_node->has_async_transaction = 0;
                else
                    list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
            }
            // 减少相关的引用计数
            binder_transaction_buffer_release(proc, buffer, NULL);\
            // 释放内核缓冲区 buffer 
            binder_free_buf(proc, buffer);
            break;
        }
        ......
}

可以看到 binder_thread_write 中对于 BC_FREE_BUFFER 协议码的处理, 主要是释放通信过程给目标进程分配的内核缓冲区, 减少相关的引用计数

接下来看看 binder_thread_write 对 BC_REPLY 的处理

BC_REPLY 协议码的处理

        ......
        // 处理 BC_TRANSACTION/BC_REPLY
        case BC_TRANSACTION:
        case BC_REPLY: {
            // 从用户空间拷贝数据到 binder_transaction_data 中
            struct binder_transaction_data tr;
            if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            // 进行指令码的处理操作
            binder_transaction(proc, thread, &tr,
                       cmd == BC_REPLY, 0);
            break;
        }
        ......
        }
}

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)
{
    ......
    if (reply) {// 处理 BC_REPLY 指令
        // 找寻目标线程(即 Client 端的线程)
        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);
        if (in_reply_to->to_thread != thread) {
            ......
            return_error = BR_FAILED_REPLY;
            in_reply_to = NULL;
            goto err_bad_call_stack;
        }
        // 将要处理的事务, 添加到线程栈的顶端
        thread->transaction_stack = in_reply_to->to_parent;
        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) {
            return_error = BR_FAILED_REPLY;
            in_reply_to = NULL;
            target_thread = NULL;
            goto err_dead_binder;
        }
        target_proc = target_thread->proc;
        
    } else {// 处理 BC_TRANSACTION 指令, 前面已经分析过了
       ......
    }
    
    // ....... 与分析 BC_TRANSACTION 后续一致
}

可以看到 Binder 驱动对于 BC_REPLY 比较简单, 除了 BC_REPLY 中的操作与 BC_TRANSACTION 有所不同, 后续的操作是一致的, 毕竟调用的是同一个方法
, 最终会封装成两个工作项 BINDER_WORK_TRANSACTION 和 BINDER_WORK_TRANSACTION_COMPLETE 分别发送给目标进程和源进程
(这里的目标进程为Client 端, 源进程为 Server 端了, 因为本次发起 Binder 驱动通信的为 Server 端)

源进程接收到 BINDER_WORK_TRANSACTION_COMPLETE 之后, 就彻底的结束这次的 Binder 通信了, 这里不再赘述

接下来看看目标进程如何处理 BINDER_WORK_TRANSACTION 工作项

Client 端处理 BINDER_WORK_TRANSACTION 工作项

// kernel/goldfish/drivers/staging/android/binder.c
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)
{
    ......
    // 循环从其读取器工作项数据
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        ......
        
        // 处理工作项中对应的指令码
        switch (w->type) {
        // 我们主要关注对 BINDER_WORK_TRANSACTION 的处理
        case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
        ......
        // 将 binder_transaction 中的数据从 binder_transaction_data 中, 以便后续可以传输到用户空间
        if (t->buffer->target_node) {
            ......// 上面已经分析过了
        } else {
            // target_node 为 NULL, 则指定协议码 BR_REPLY
            tr.target.ptr = 0;
            tr.cookie = 0;
            cmd = BR_REPLY;
        }
        .......
        // 将协议码和数据拷贝到用户空间
        if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);
        .......
        // 这个工作项已经被处理了, 从链表中删除
        list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        // 判断是否为同步请求
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            // ...... 在上面已经分析过了
        } else {
            // 直接释放内核缓冲区的内存
            t->buffer->transaction = NULL;
            kfree(t);
        }
        break;
    }
    return 0;
}

可以看到 BINDER_WORK_TRANSACTION 工作项的 t->buffer->target_node 为 NULL 时, 会将协议码置为 BR_REPLY, 然后将数据写入用户空间, 接下来我们看看用户空间对 BR_REPLY 的处理

Client 端对 BR_REPLY 的处理

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        // 从输入缓冲区中读取, 是否有 Binder 驱动写入的数据
        cmd = (uint32_t)mIn.readInt32();
        // 主要查看 BR_TRANSACTION_COMPLETE 指令码
        switch (cmd) {
        case BR_REPLY:
            { 
                // 从用户缓冲区获取 Binder 驱动写入的数据
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                if (err != NO_ERROR) goto finish;
                if (reply) {
                    // 表示该线程发送的进程间通信请求已经被处理了
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        // 这个方法将 Binder 驱动传递过来的数据写入 Parcel 的 reply 中
                        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 {
                        ......
                    }
                } else {
                   ......
                }
            }
            // 跳出循环, 即 waitForResponse 等待进程通信的结果的操作已经结束了
            goto finish;
        ......
    }

finish:
    ......
    
    return err;
}

可以看到 Client 端用户空间对 BR_REPLY 的操作也非常清晰

Binder 进程通信回顾

至此, 一次 Binder 进程间的通信就分析完了, 这里再次梳理一下, 其主要包括如下几个步骤

时序图

Binder 驱动通信时序图.png

总结

至此, 我们 Binder 驱动的讲解就结束了, 这部分的内容较之音视频的难度, 感觉也不遑多让, 笔者花费了很大的精力去剖析, 的确非常的晦涩难懂, 这也是笔者为何迟迟难以下笔的原因

若有人能够坚持读到这里, 那这可真是相当令人欣慰的事情啊, 若能够帮你理清 Binder 驱动相关知识, 那我将会感到非常的荣幸

这里祝大家新年快乐了, 希望新的一年里, 大家都能收获自己想要的, 越努力, 越幸运 !

上一篇 下一篇

猜你喜欢

热点阅读