前言

通过 Binder 上一篇文章的分析, 我们知道了 Binder 驱动在我们应用开发过程中的使用方式, 了解到了 BBinderBpBinder 两个非常重要的 Native 对象, 本次我们就着重分析一下 Binder 在运行时库层知识
因为运行时库是使用 C/C++ 编写的, 对于 Android 开发者来说, 可能有些晦涩难懂, 笔者也下了很大的功夫, 对此感兴趣的同学可以耐心往下读, 如果存在让大家兴奋的点, 那么笔者就十分满足了

  • 应用层框架层的 Binder 库
  • ServiceManager 的启动
  • ServiceManager 进程间通信

一. AndroidRuntime 层的 Binder 库

封装的意义

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

Binder 库关键类

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

  • Service: BnInterface, 即 Binder native interface.
  • Client: BpInterface, 即 Binder proxy interface.

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();
}
复制代码
  • 模板参数 INTERFACE 是一个由进程自定义的 Service 组件接口, 模板类 BnInterface 需要实现该接口
  • 该类由又继承了 BBinder

接下来分析一下 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

  • transact: 当 Binder 代理对象通过 Binder 驱动程序向一个 Binder 本地对象发出一个进程间的请求时, Binder 驱动程序就会调用该 Binder 本地对象的成员函数 transact 来处理该请求
  • onTransact: 该方法由 Binder 本地对象来实现, 它负责分发与业务相关的进程间的请求
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 来获取

  • mHandle: Client 组件就是通过这个句柄值来和 Binder 驱动程序中的 Binder 引用对象 binder_ref 建立对应关系

3. IPCThreadState

  • 每一个使用了 Binder 进程间通信的进程, 都有一个 Binder 线程池, 用来处理进程间的通信请求
  • 对于每一个线程来说, 它的内部都有一个 IPCThreadState 对象, 我们可以通过 IPCThreadState 类的静态方法 self 来获取
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

  • 对于每一个使用了 Binder 进程间通信机制的进程来说, 它的内部都存在一个 ProcessState 对象

接下来看看 ProcessState 的实现

4. ProcessState

class ProcessState: public virtual RefBase
{
public:static sp<ProcessState> self();......
private:int mDriverID;void* mVMStart;
}
复制代码
  • ProcessState 这个对象与当前使用 Binder 通信的进程一一对应, 它主要负责

    • 通过 Binder 驱动打开 binder 设备文件 dev/binder
    • 将设备文件 dev/binder 映射到进程的地址空间
    • 每一个 Binder 线程池里的线程都可以通过它来与 Binder 驱动 建立连接

应用层 Binder 库的 UML 图

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

二. ServiceManager 的启动

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

  • Service Manager 是 Binder 进程间通信的核心组件之一
  • 它扮演着 Binder 进程间通信机制的上下文管理者的角色
  • 同时负责管理系统中的 Service 组件, 并且向 Client 组件提供获取 Service 代理对象的服务

由于篇幅原因, 这里就不介绍 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.cstruct 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 的打开设备文件的操作非常简单

  • 调用 open 函数打开 Binder 设备文件

    • 会调用 binder 驱动的 binder_open 打开设备文件, 返回一个 file 设备文件结构体的句柄值
  • 将给进程分配的内核缓冲区大小记录到 binder_state 结构体对象中
  • 调用函数 mmap 将设备文件 /dev/binder 映射到地址空间
    • 返回为其分配的地址空间的首地址
    • 记录到 binder_state 结构体对象中

注册为 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_SET_CONTEXT_MGR 为 IO 控制命令

    • 这个标记位代表将当前进程注册为 binder context manager 即 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 这个控制码, 主要做了以下操作

  • 将为这个请求成为 Binder 上下文管理者的进程创建其对应的 binder 实体对象
  • 保存在内核驱动的静态变量 binder_context_mgr_node 中

循环等待处理 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 主要做了以下几件事情

  • 通过 readbuf 记录 BC_ENTER_LOOPER 控制码, 将当前线程注册成为 Binder 线程, 以便 Binder 驱动程序可以将进程间的通信请求分发给它处理
  • 通过 binder_write 将 readbuf 控制码发送给 binder 驱动处理, 其内部同样是使用 ioctrl 与 binder 驱动通信
  • for 循环不断的从 binder 驱动中获取新的进程间通信请求

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_READres = 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()

  • 我们调用了 BpServiceManager 这个 BpBinder 对象的 addService() 方法
  • 至此一个跨进程的请求就成功发起了

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

    // frameworks/base/libs/binder/IServiceManager.cppvirtual 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 的 transactstatus_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);// 3. 读取请求结束后 Server 通过 Binder 驱动返回回来的数据return err == NO_ERROR ? reply.readExceptionCode() : err;}
复制代码

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

  • 将通信参数封装到 Parcel data 中

    • 写入接口对应的描述
    • 写入传入的形参
  • 通过 BpBinder 的 transact 向 Binder 驱动发起跨进程调用请求
  • 读取请求结束后 Server 通过 Binder 驱动返回回来的数据

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_TRANSACTIONerr = waitForResponse(reply);} else {......}} else {......}return err;
}
复制代码

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

  • 通过 writeTransactionData 函数将 data 等数据封装成为一个 binder_transaction_data 对象, 用来和 Binder 驱动程序交互

    • 与 Binder 驱动程序交互的操作码为 BC_TRANSACTION
  • 通过 waitForResponse 函数, 向 Binder 驱动发起请求

先看看如何将 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;elseerr = -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);elsemOut.setDataSize(0);}// 将 Binder 驱动返回的命令协议保存到 mIn 中if (bwr.read_consumed > 0) {mIn.setDataSize(bwr.read_consumed);mIn.setDataPosition(0);}return NO_ERROR;}return err;
}
复制代码

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

  • 将 IPCThreadState 中的输出缓冲区 mOut 和输入缓冲区 mIn 封装到 binder_write_read 这个结构体对 bwr 中
  • 将 bwr 传入 ioctl 函数, 通过 ioctl 函数与 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_TRANSACTIONif (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);......switch(cmd) {......// 处理 BC_TRANSACTION/BC_REPLYcase 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));// 初始化 tif (!reply && !(tr->flags & TF_ONE_WAY))t->from = thread;elset->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 它主要做了如下操作

  • 获取 Client 调用的 binder 引用对象
  • 通过 binder 引用对象找到对应的 binder_node 实体对象
  • 通过 binder 实体对象, 找对对应的目标 Server 进程
  • 在 Server 进程找到最合适的空闲线程去处理这次 Client 端的请求
  • 将目标线程的 todo 队列和 wait 队列保存到成员变量中
  • 初始化 t 和 tcomplete 对象
    • t 交由目标进程处理
    • tcomplete 交由源进程处理

接下来就要处理方法的调用了, 我们在应用层调用了 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 实体对象
  • 没有实体对象则调用 binder_new_node 在源进程创建一个
  • 在目标进程中获取 Binder 引用对象, 没有则创建一个

好的, 线程参数数据也已经获取完了, 我们接着往下看 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;} elsetarget_node->has_async_transaction = 1;}// 将 t 的工作项设置为 BINDER_WORK_TRANSACTIONt->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;复制代码

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

  • 将 BINDER_WORK_TRANSACTION 工作项发送到目标线程的工作队列中
  • 将 BINDER_WORK_TRANSACTION_COMPLETE 工作项发送到源线程的工作队列中

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 处理也非常简单

  • 将 BR_TRANSACTION_COMPLETE 从 Linux 内核中投递到用户空间
  • 所以在用户空间的 waitForResponse 中可以通过 mIn 输入缓冲区读取到 binder 内核驱动传递过来的 BR_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 指令码很简单

  • 跳出了 waitForResponse 的方法
  • 该操作会回到其上一级方法 talkWithDriver 中, 继续循环等待目标进程将上次发出的进程间通信请求返回回来

所以, 接下来的重头戏便是我们需要查看目标线程对 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_TRANSACTIONstruct 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 驱动中做了如下的事情

  • 从目标线程的 todo 队列中获取工作项, 并且将数据存入 binder_work 结构体对象中
  • 对与 BINDER_WORK_TRANSACTION 这个指令码, 主要是将 binder_work 结构体转为了 binder_transaction 结构体对象 t
  • 将 t 中的数据拷贝到 tr 这个结构体对象中, 以便于后续可以将其传输到用户空间
  • 将 tr 中的数据拷贝用用户空间
  • 工作项处理完毕, 将其从 todo 队列中移除

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

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 和 rdatabio_init(&reply, rdata, sizeof(rdata), 4); // 4. 解析 txn 中的数据到 msg 中bio_init_from_txn(&msg, txn);// 5. 调用 func 函数指针, 处理协议, 将结果写入 replyres = func(bs, txn, &msg, &reply);......// 将通信结果返回给 binder 驱动binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);}ptr += sizeof(*txn);break;}}}
}
复制代码

binder_parse 中所做的事情非常清晰

  • 读取协议码
  • 关于 BR_TRANSACTION 协议码
    • 先获取进程间通信的数据 txn
    • 将 txn 中的数据解析到 binder_io 对象 msg 中
    • 调用 func 这个函数指针, 真正的执行协议的处理
    • 调用 binder_send_reply 将通信结果返回给 binder 驱动

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_BUFFERdata.cmd_free = BC_FREE_BUFFER;data.buffer = buffer_to_free;// cmd_reply 的协议为 BC_REPLYdata.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 中做的操作也比较清晰

  • 写入 BC_FREE_BUFFER 协议码
  • 写入 BC_REPLY 协议码
  • 最终调用了 binder_write 函数
    • 其内部调用 ioctl() 函数和 IO 控制命令 BINDER_WRITE_READ 将 BC_FREE_BUFFER/BC_REPLY 发送给 binder 驱动程序

接下来看看 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_TRANSACTIONif (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;elselist_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_REPLYcase 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_REPLYtr.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 的操作也非常清晰

  • 从用户空间获取数据, 写入 tr 中
  • 将 tr 中的数据写入 Parcel 的 reply 中
  • 跳出循环, 即 waitForResponse 函数执行结束, 本次 Binder 进程间通信结束

Binder 进程通信回顾

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

  • Client 端向 Binder 驱动发起 BC_TRANSACTION 协议码
  • Binder 驱动处理 BC_TRANSACTION 协议, 产生了两个工作项
    • BINDER_WORK_TRANSACTION_COMPLETE

      • Binder 驱动将 BR_TRANSACTION_COMPLETE 协议码, 发送给 Client 端
    • BINDER_WORK_TRANSACTION
      • Binder 驱动将 BR_TRANSACTION 协议码, 发送给 Server 端
  • Client 端处理 BR_TRANSACTION_COMPLETE 协议, 继续等待通信结果
  • Server 端处理 BR_TRANSACTION 协议
    • 调用 Client 端请求的方法, 得到返回值写入 reply
    • 将 BC_REPLY 发送给 Binder 驱动
  • Binder 驱动处理 BC_REPLY 协议码, 产生了两个工作项
    • BINDER_WORK_TRANSACTION_COMPLETE

      • Binder 驱动将 BR_TRANSACTION_COMPLETE 协议码, 发送给 Server 端
    • BINDER_WORK_TRANSACTION
      • Binder 驱动将 BR_REPLY 协议码, 发送给 Client 端
  • Server 端处理 BR_TRANSACTION_COMPLETE 协议码, 表示通信结束
  • Client 端处理 BR_REPLY 协议码, 得到通过结果
  • 至此一次通信结束

时序图

总结

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

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

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

Binder 驱动详解(下)相关推荐

  1. Binder机制详解(一)

    系列目录 Binder机制详解(二) Binder机制详解(三) 文章目录 前言 一.爱情例子 1.普通Linux间进程通信方式 2.接着讲爱情的例子(Binder的实现机制) 3.Binder少拷贝 ...

  2. 使用VS2010编译MongoDB C++驱动详解

    最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...

  3. Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令)

    Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令) 原创 2017年03月01日 22:58:39 标签: 开发人员 / UAV / 软件 / 硬件 一.开篇 开源精神常在!!! 谁说软件 ...

  4. MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解

    Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...

  5. linux usb gadget驱动详解(一)

    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...

  6. Z-STACK之cc2530LED驱动详解

    Z-STACK 之LED驱动详解      最近一段时间学习ZigBee,用的TI公司的cc2530,协议栈是z-stack,为了深入了解整个Z-stack,我从底层的驱动代码开始看起,首先是LED驱 ...

  7. 博通wifi驱动详解

    1        WLAN技术 WLAN是英文WirelessLAN的缩写,就是无线局域网的意思.无线以太网技术是一种基于无线传输的局域网技术,与有线网络技术相比,具有灵活.建网迅速.个人化等特点.将 ...

  8. LCD液晶屏驱动详解

    开发环境: 开发板:JZ2440V3 CPU:samsunS3C2440 内核:Linux3.4.2 编译工具:arm-linux-gcc 4.3.2 LCD:4.3存液晶屏AT043TN24 参考文 ...

  9. nvme 驱动详解[转]

    nvme 驱动详解 之1 http://blog.csdn.net/qqqqqq999999/article/details/47732319 首先打开driver/block下的kconfig文件, ...

最新文章

  1. 电机高频注入原理_永磁同步电机新型转子位置估计误差补偿策略
  2. Android配置环境问题
  3. php运行环境largon,环境配置(一)
  4. 双11稳定性负责人叔同讲述:九年双11的云化架构演进和升级
  5. 信息学奥赛一本通C++语言——1094:与7无关的数
  6. 对象交互。英雄搏击游戏。0107
  7. 主人公的出场—一个程序员的成长史(1)
  8. PL/SQL中,declare定义变量和variable定义变量的区别?
  9. Java EE实战教程 servlet (一)
  10. 错误解决:java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to star
  11. 先码后看,程序员的「双节」该买点什么?
  12. QQ登录之后自动弹出“QQ网吧”怎么屏蔽?
  13. PHP手机深色模式,哪些手机深色模式比较好?六大主流品牌手机深色模式对比介绍...
  14. 机器学习技法-01-2-Large-Margin Separating Hyperplane
  15. iOS 页面切换控制
  16. 计算机电源风扇安装方法,机箱风扇怎么装 电脑机箱风扇电源线接法
  17. java float 大小比较_java浮点型比较大小
  18. 什么是RST包,什么是三次握手,什么是四次握手
  19. Android开发详解之App升级程序一点通
  20. 混合硬盘计算机,什么是混合硬盘 什么是hhd硬盘?

热门文章

  1. 用python画烟花-用python实现漂亮的烟花demo
  2. python3下载手机安卓版-QPython3下载
  3. python主要用于什么-python主要用于哪些方向
  4. python pandas读取excel-Python使用Pandas读写Excel实例解析
  5. python3下载教程-Python3 教程
  6. 图片的裁剪、旋转、平移、模糊
  7. UVa483 Word Scramble
  8. LeetCode Implement strStr(kmp或者BM)
  9. Linux应用总结(1):自动删除n天前日志
  10. HashMap、TreeMap、Hashable和LinkedHashMap