大数据

Binder 学习笔记

这不是一篇详细介绍 Binder 实现原理的文章,因为介绍 Binder 的相关文章已经非常多了,比如 :
Android深入浅出之Binder机制
Android Bander设计与实现 – 设计篇
Android Binder机制系列文章
但是作为一个普通的 Android 程序员,尤其是不熟悉 C语言,Linux内核及驱动,JNI 的程序员,面对大量的底层源码分析,还是多次中途放弃。

这次终于一鼓作气看完了 Android Binder机制系列文章,总算搞清了 Binder 的来龙去脉,所以这里整理记录下来。如果你也想彻底搞清 Binder 的设计与实现,那么可以Android Binder机制系列文章为主进行学习,遇到困惑的地方可以参考本文,说不定会有帮助。

2 源码及工具准备

2.1 源码下载

我们的目的只是为了查看 Binder 源码,所以不需要安装 repo,也不用下载整个的 AOSP 项目,只要下载一些我们会用到的源码就好了。
所有的Android的源码都可以在这里下载 https://android.googlesource.com/
下面列出 Binder学习中需要用到的几个包:

下载方法:点击上面链接,打开后选择任意一个分支,点击tgz链接,就可以下载源码压缩包了。有些源码已经跟很多博客中分析的不一样了,不过改动都不大。一般下载master分支就可以了,不过 kernel/common 的master分支是空的,可选择 android-4.4 分支下载。

下载源码压缩包

2.2 源码阅读工具

在阅读 Binder 源码时,经常需要查看各种方法的调用,类的定义等等,需要频繁的查找和跳转,所以一个顺手的工具可以大大提高阅读效率。
推荐使用 Sublime Text 3,mac/windows通吃。装好之后还需要安装 Ctags 插件,用于建立其代码索引,可以方便的跳转查看方法的实现。

常用操作:

  • Ctrl + P,输入文件名称,回车打开文件
  • Ctrl + P,输入 @+函数名,查找某个函数

3 Binder 概述

3.1 什么是 Binder

在不同的上下文环境中提到 binder,代表的意思也不同:

  • 从 Android 系统层面来说,binder 指的一种实现进程间通讯的机制
  • 在 Linux 内核中, binder 指的是一个虚拟设备的驱动,可以想象成两个进程间通讯的管道,用户态的进程 A 只需要通过系统调用向 binder 驱动发送命令,binder 驱动就会搞定一切,把命令传送给指定的进程 B,并把处理结果返回给 A。 binder 封装了底层对线程、内存复杂的管理操作,暴露给用户进程的只是几个命令,极大的简化了进程间的通讯。

3.2 Binder 整体架构

Binder 的设计整体上是典型的 C/S 架构,Binder 架构中的4个角色完全可以拿典型的 TCP/IP 网络结构来类比:

  • Binder Client:发起服务请求的客户端
  • Binder Server:响应服务请求的服务端
  • ServiceManager:提供查找服务,添加服务的DNS
  • Binder 驱动:定义通讯协议,完成数据的封装和传输的TCP/IP通讯协议

3.3 Binder 典型交互

  1. Server 将自己注册到 ServiceManager 中
  2. Client 通过服务名称向 ServiceManager 查找对应服务
  3. ServiceManager 根据服务名称查找到 Server ,在内核中建立对应的 Binder 实体,并将该实体对应的 Binder 引用返回给Client
  4. Client 通过 Binder 引用向 Server 发起请求
  5. Binder 驱动根据 Binder 引用找到对应的 Binder 实体,将客户端的请求发送给 Binder 实体处理
  6. Binder 驱动将处理后的结果返回给 Client

接下来将对 Binder 架构中的关键环节的流程进行一一个梳理

4 ServiceManager

ServiceManager 作为注册和查找服务的中心,其实本质上也是一个特殊的 server,只是它必须要先于其他的 server 启动,这样其他的 server 在初始化的时候就可以把自己注册到 ServiceManager 中。

通过在 init.rc 添加启动脚本,系统在启动初始化时就会调用 service_manager 的 main 入口函数完成 ServiceManager 的启动和初始化:

platform/native/cmds/servicemanager/service_manager.c int main() { struct binder_state *bs; bs = binder_open(128*1024); //1.打开 binder 驱动,获取文件描述符 // 略 if (binder_become_context_manager(bs)) { // 2.将 ServiceManager 注册成为“DNS” ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } // 略 binder_loop(bs, svcmgr_handler); // 3.进入循环等待接收客户端发来的请求 return 0; }

略去无用代码,关键的部分只有三步,注释已经写明,下面看看每一步具体做了什么

4.1 binder_open()

platform/native/cmds/servicemanager/binder.c struct binder_state *binder_open(size_t mapsize) { struct binder_state *bs; struct binder_version vers; bs = malloc(sizeof(*bs)); ... bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC); 1. 打开 binder 设备驱动 ... } ... bs->mapsize = mapsize; bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); // 2.建立内存映射 } return bs; }

open 函数是一个系统函数,用于打开第一个参数指明的设备,对应的函数在 binder 驱动 /kernel/common/drivers/android/binder.c(注意跟 servicemanager 目录下的binder.c区分,后者只是封装了一些 servicemanager 跟 binder 驱动交互的操作)的源码中定义

kernel/common/drivers/android/binder.c static const struct file_operations binder_fops = { .owner = THIS_MODULE, .poll = binder_poll, .unlocked_ioctl = binder_ioctl, .compat_ioctl = binder_ioctl, .mmap = binder_mmap, .open = binder_open, .flush = binder_flush, .release = binder_release, };

这个结构体定义了open 函数对应的函数指针,所以实际的函数是 binder_open,这里不展开了主要是理清流程。
接下来 mmap 操作完成用户内存到内核内存的映射。

4.2 binder_become_context_manager()

该函数内部通过 ioctl 函数向 binder 驱动发送一个 BINDER_SET_CONTEXT_MGR 的命令,使当前进程成为系统的 ServiceManager。该命令的处理在 /kernel/common/drivers/android/binder.cbinder_ioctl 函数中,binder 驱动会为建立 ServiceManager 对应的 binder 实体。(注意:这里的实现已经跟多数文章中讲的不同,不再是用一个全局变量保存该 binder 实体,而是保存在 binder_proc->binder_context->binder_context_mgr_node这个变量中)

4.3 binder_loop()

platform/native/cmds/servicemanager/binder.c void binder_loop(struct binder_state *bs, binder_handler func) { readbuf[0] = BC_ENTER_LOOPER; binder_write(bs, readbuf, sizeof(uint32_t)); for (;;) { res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); } }

主要的操作就只有上面几行代码,其余的都已省略。
binder_write函数会调用 ioctl 发送 BC_ENTER_LOOPER 命令告诉 binder 驱动当前线程已进入消息循环状态。接下来的死循环,从 binder 驱动读取消息到 &bwr,如果没有消息就会阻塞直到被唤醒,读取到消息后再调用binder_parse解析 &bwr 中的消息内容。
binder_parse会调用 func 函数处理请求。func 参数传入的值是一个指向svcmgr_handler函数的函数指针,所以具体的如添加查找函数的请求处理主要都在 svcmgr_handler函数中了。
处理完请求回到binder_parse 后调用binder_send_reply向驱动返回处理的结果(根据请求类型如果不需要回复就没有这一步)

5 获取 ServiceManager

客户端想要获取系统服务,首先就要向 ServiceManager 请求服务端的代理对象,而各类系统服务 也要在系统启动时先注册到 ServiceManager。所以如何获取 ServiceManager 就成为了第一个要解决的问题。
IServiceManager::defaultServiceManager() 就是用来获得一个 ServiceManager 服务代理的函数。

platform/native/libs/binder/IServiceManager.cpp sp defaultServiceManager() { if (gDefaultServiceManager != NULL) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); while (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast( ProcessState::self()->getContextObject(NULL)); if (gDefaultServiceManager == NULL) sleep(1); } } return gDefaultServiceManager; }

一个典型的单例模式,关键就是下面这一句了,信息量很大,一点一点拆开来看

gDefaultServiceManager = interface_cast(ProcessState::self()->getContextObject(NULL));

5.1 ProcessState::self()

这里又是一个单例模式获取一个 ProcessState 对象,在构造函数中,打开了 /dev/binder 设备,保存了文件句柄。然后调用mmap()映射内存到当前进程的虚拟地址空间。

5.2 getContextObject()

实际上调用了 getStrongProxyForHandle(handle),这个函数返回一个 BpBinder(handle),这里 handle 为0,也即 ServiceManager 的句柄值。

5.3 interface_cast

这部分是最暗藏玄机也比较不好理解的部分,前面我们已经获得了一个 BpBinder 对象,而这里最终要返回一个 IServiceManager 对象,然而这两者并没有任何的继承关系,所以肯定不是简单的强转。看这个模板函数的定义。

frameworks/native/include/binder/IInterface.h template inline sp interface_cast(const sp& obj) { return INTERFACE::asInterface(obj); }

INTERFACE 替换成 IServiceManager,然而 IServiceManager 中并没有 asInterface 这个方法。注意到 IServiceManager.h 中有一句宏调用:

DECLARE_META_INTERFACE(ServiceManager)

IServiceManager.cpp 中也有一句宏调用:

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");

这里的声明和实现里有包括了 asInterface 方法,这两个宏定义在 IInterface.h 中:

frameworks/native/include/binder/IInterface.h #define DECLARE_META_INTERFACE(INTERFACE) \ static const android::String16 descriptor; \ static android::sp<I##INTERFACE> asInterface( \ const android::sp& obj); \ virtual const android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \ #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ const android::String16 I##INTERFACE::descriptor(NAME); \ const android::String16& \ I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ const android::sp& obj) \ { \ android::sp<I##INTERFACE> intr; \ if (obj != NULL) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ if (intr == NULL) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { }

把两个宏展开之后就变成了:

#define DECLARE_META_INTERFACE(IServiceManager) \ static const android::String16 descriptor; \ static android::sp asInterface( \ const android::sp& obj); \ virtual const android::String16& getInterfaceDescriptor() const; \ IServiceManager(); \ virtual ~IServiceManager(); \ #define IMPLEMENT_META_INTERFACE(IServiceManager, "android.os.IServiceManager") \ const android::String16 IServiceManager::descriptor("android.os.IServiceManager"); \ const android::String16& \ IServiceManager::getInterfaceDescriptor() const { \ return IServiceManager::descriptor; \ } \ android::sp IServiceManager::asInterface( \ const android::sp& obj) \ { \ android::sp intr; \ if (obj != NULL) { \ intr = static_cast<IServiceManager*>( \ obj->queryLocalInterface( \ IServiceManager::descriptor).get()); \ if (intr == NULL) { \ intr = new BpServiceManager(obj); \ } \ } \ return intr; \ } \ IServiceManager::IServiceManager() { } \ IServiceManager::~IServiceManager() { }

我们重点关注 asInterface 方法,这里传入的 obj 参数就是前面得到的 BpBinder(0) 对象,queryLocalInterface 的默认实现在 IBinder.cpp 中,它返回 null,所以这里 intr == null,最终就构造了 BpServiceManager(obj) 返回,而BpServiceManager 正是 IServiceManager 的一个实现类,它也是 ServiceManager 在客户端的代理对象。

5.4 总结

借用 这篇文章 的一张图来梳理一下 Binder 架构中各个类的关系:

  1. 服务接口定义服务提供的业务逻辑,比如 IServiceManager,定义了 getService,addService 等。
  2. 本地服务,也即服务端,是真正实现业务逻辑的对象。
  3. 远程服务,也即客户端,或者代理端,比如 BpServiceManager,里面保存了一个 BpBinder(实际在构造函数时保存在父类 BpRefBase.mRemote 中,通过 remote() 方法获得),作为服务端的代理,主要职责是封装请求,并向 Binder 驱动发起请求,Binder 驱动会负责把请求发送到对应的服务端。这部分后面还会再做讲解
  4. Binder 驱动收到代理发起的请求,最终会调用到 BBinder.onTransact 方法,所以本地服务只要实现 onTransact 方法就能处理客户端发来的请求了,这部分后面还会再做讲解。
  5. BpInterface 和 BnInterface 都定义在 Interface.h 中,这里的 INTERFACE 即是服务接口。

6 注册服务到ServiceManager

获取到 IServiceManager,就可以调用其 addService 方法,将服务添加到 ServiceManager 了,注意这时候服务扮演的是客户端的角色,ServiceManager 扮演的是服务端的角色。

6.1 addService

platform/native/libs/binder/IServiceManager.BpServiceManager virtual status_t addService(const String16& name, const sp& service, bool allowIsolated) { Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name); data.writeStrongBinder(service); data.writeInt32(allowIsolated ? 1 : 0); status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); return err == NO_ERROR ? reply.readExceptionCode() : err; }

可以看到这里把请求的参数打包到 Parcel 中(关于 Parcel 打包数据的原理这里就不具体讲了,可以参考这里),注意 data.writeStrongBinder(service); 这句,这里的 service 是一个 BnXXX,也即一个本地服务对象,在写入的时候会被转换为一个 flat_binder_object 结构体写入,关于这个结构体的说明,参见 这里
接下来remote()->transact(),这里的 remote() 前面已经说了,返回之前保存进去的 BpBinder,BpBinder 的 transact 方法最终调用到 IPCThreadState::self()->transact(),到这里,实际跟 binder 驱动打交道的类 `IPCThreadState 终于出现了。

6.2 IPCThreadState.transact()

IPCThreadState::self() 获取调用进程的 IPCThreadState 的单例,如果首次调用,会进行打开binder驱动,内存映射等操作,和前面ServiceManager的启动类似。
精简后保留主要逻辑的 transact() 代码:

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) { err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); } if ((flags & TF_ONE_WAY) == 0) { if (reply) { err = waitForResponse(reply); } } return err; }

可以看到主要涉及两个函数 writeTransactionDatawaitForResponse

6.3 writeTransactionData()

这里必须先介绍 IPCThreadState 两个 Parcel 类型的成员变量,mIn 和 mOut,分别用来保存从 binder 驱动读取的数据和即将向 binder 驱动写入的数据。而 writeTransactionData() 这个函数的作用就是把数据二次封装到 mOut 中,这里又必须借一张图来说明了:

有效数据对应传入的 data 参数,也就是在6.1节封装的那个 Parcel,而经过writeTransactionData() 的再次封装之后, mOut = binder_transaction_data 结构 + BC_TRANSACTION(一个整数值)。后面会看到在发给 binder 驱动之前数据还会经过一层封装到 binder_write_read 结构中。

6.4 waitForResponse()

精简后保留主要逻辑的代码:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) { uint32_t cmd; int32_t err; while (1) { if ((err=talkWithDriver()) < NO_ERROR) break; if (mIn.dataAvail() == 0) continue; cmd = (uint32_t)mIn.readInt32(); switch (cmd) { case BR_TRANSACTION_COMPLETE: if (!reply && !acquireResult) goto finish; break; case BR_REPLY: { 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) { reply->ipcSetDataReference( reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), freeBuffer, this); } else { err = *reinterpret_cast(tr.data.ptr.buffer); freeBuffer(NULL, reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), this); } } else { freeBuffer(NULL, reinterpret_cast(tr.data.ptr.buffer), tr.data_size, reinterpret_cast(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), this); continue; } } goto finish; default: err = executeCommand(cmd); if (err != NO_ERROR) goto finish; break; } } finish: if (err != NO_ERROR) { if (acquireResult) *acquireResult = err; if (reply) reply->setError(err); mLastError = err; } return err; }

这里的逻辑相对复杂,不过整体上看,主要是一个死循环,当满足某些特定条件时才会跳出,下面具体分析死循环里的几个关键步骤。

6.4.1 第一次调用talkWithDriver()

精简后的代码如下:

status_t IPCThreadState::talkWithDriver(bool doReceive) { binder_write_read bwr; // Is the read buffer empty? const bool needRead = mIn.dataPosition() >= mIn.dataSize(); // We don't want to write anything if we are still reading // from data left in the input buffer and the caller // has requested to read the next data. const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; bwr.write_size = outAvail; bwr.write_buffer = (uintptr_t)mOut.data(); /*****第1步*****/ // This is what we'll read. if (doReceive && needRead) { bwr.read_size = mIn.dataCapacity(); bwr.read_buffer = (uintptr_t)mIn.data(); } else { bwr.read_size = 0; bwr.read_buffer = 0; } // Return immediately if there is nothing to do. if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; bwr.write_consumed = 0; bwr.read_consumed = 0; status_t err; do { #if defined(__ANDROID__) /*****第2步*****/ if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) err = NO_ERROR; else err = -errno; #else err = INVALID_OPERATION; #endif } while (err == -EINTR); if (err >= NO_ERROR) { /*****第3步*****/ if (bwr.write_consumed > 0) { if (bwr.write_consumed < mOut.dataSize()) mOut.remove(0, bwr.write_consumed); else mOut.setDataSize(0); } if (bwr.read_consumed > 0) { mIn.setDataSize(bwr.read_consumed); mIn.setDataPosition(0); } return NO_ERROR; } return err; }

代码看起来很长,但是其实主要就分为一下几步,已在代码中标出:

  1. 把数据包装到一个 binder_write_read 结构的变量 bwr
  2. 调用 ioctl 向binder驱动发送指令,binder驱动会从 bwrwrite_buffer 读取数据,经过处理之后,如果有返回,就写入到 read_buffer
  3. 根据 bwr 中的数据更新 mOutmIn 的数据

注:这里的第2步这里只是一笔带过,其实包含了和binder驱动的交互的关键步骤,为了避免打断分析的流程,这里就不深入了,具体可以参考
Android Binder机制(五) addService详解01之 请求的发送
Android Binder机制(六) addService详解02之 请求的处理
Android Binder机制(七) addService详解03之 请求的反馈
这三篇文章。这里我们只需要知道,binder驱动把请求发送给 ServiceManager后,就返回了 BR_NOOP 和 BR_TRANSACTION_COMPLETE 两条指令,注意这里返回时只代表驱动已经把请求发送给 ServiceManager处理,而处理的结果此时还没返回。

6.4.2 读取 BR_NOOP 和 BR_TRANSACTION_COMPLETE

上一节第一次调用talkWithDriver()后,收到了binder驱动返回的 BR_NOOP 和 BR_TRANSACTION_COMPLETE 两条指令,回到waitForResponse()的死循环,接着循环两次从mIn中读取两条指令,这两条指令都是什么都不做。

6.4.3 进入中断等待

读取完两条返回的指令后,再次进入 waitForResponse() 的死循环,此时:

bwr.write_size = 0; bwr.write_buffer = (long unsigned int)mOut.data(); bwr.write_consumed = 0; bwr.read_size = mIn.dataCapacity(); // 256字节 bwr.read_buffer = (long unsigned int)mIn.data(); bwr.read_consumed = 0;

再次进入 talkWithDriver()->ioctl(),此时因为bwr.write_size = 0 代表没有数据要写入,然后bwr.read_consumed = 0 代表没有数据要读取,此时就会进入中断等待状态,具体查看 binder驱动源码中 binder_thread_read() 方法的实现。

6.5 总结

概括起来,向binder发起一个请求的过程主要包括:

  1. 数据的层层封装,Parcel –> binder_transaction_data –> binder_write_read
  2. 通过 IPCThreadState.transact() –> waitForResponse() –> talkWithDriver() –> ioctl() 向binder驱动发起请求
  3. binder驱动会按照先写后读的顺序处理请求,唤醒服务端的进程处理请求,同时向客户端返回 BR_TRANSACTION_COMPLETE 代表已经将客户端的请求发送给服务端
  4. 如果客户端没有别的请求,就陷入中断等待
  5. 服务端处理完请求,又将跟 binder 驱动通讯,binder驱动将唤醒客户端,并把处理结果返回给客户端

7 The End

关于 binder 机制的整体流程的总结总算写完了,其中还涉及很多细节没有涵盖,不过作为一个备忘,起码以后再看起来,可以很快的理清思路,再需要找到实现细节也不难了。最后还是要感谢 Android Binder机制系列文章 的作者,写的真是不能再详细了!