从源码角度看Binder

从源码角度看Binder

 

RickAi+ 关注

预计阅读时间148分钟2 年前

简介

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

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

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

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

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

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

整体图

设计架构

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

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

数据结构鸟瞰

实体型结构:

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

数据型结构:

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

Binder设计基础

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

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

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

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • IPCThreadState::IPCThreadState()

  • : mProcess(ProcessState::self()),

  • mMyThreadId(gettid()),

  • mStrictModePolicy(0),

  • mLastTransactionBinderFlags(0)

  • {

  • ...

  • }

frameworks/native/libs/binder/ProcessState.cpp

 
  • static int open_driver()

  • {

  • int fd = open("/dev/binder", O_RDWR | O_CLOEXEC);

  • ...

  • return fd;

  • }

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

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

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

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

 
  • static int binder_ioctl_write_read(struct file *filp,

  • unsigned int cmd, unsigned long arg,

  • struct binder_thread *thread)

  • {

  • ...

  • void __user *ubuf = (void __user *)arg;

  • struct binder_write_read bwr;

  • ...

  • // 从用户空间拷贝数据到内核空间

  • if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {

  • ret = -EFAULT;

  • goto out;

  • }

  • ...

  • // 从内核空间拷贝数据到用户空间

  • copy_to_user(ubuf, &bwr, sizeof(bwr)

  • }

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

Binder中的ONEWAY与非ONEWAY调用

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • status_t IPCThreadState::transact(int32_t handle,

  • uint32_t code, const Parcel& data,

  • Parcel* reply, uint32_t flags)

  • {

  • ...

  • if ((flags & TF_ONE_WAY) == 0) {

  • ...

  • if (reply) {

  • err = waitForResponse(reply);

  • } else {

  • Parcel fakeReply;

  • err = waitForResponse(&fakeReply);

  • }

  • ...

  • } else {

  • err = waitForResponse(NULL, NULL);

  • }

  •  
  • return err;

  • }

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

 
  • status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

  • {

  • uint32_t cmd;

  • int32_t err;

  • while (1) {

  • // 调用talkWithDriver与binder driver进行通信

  • if ((err=talkWithDriver()) < NO_ERROR) break;

  • ...

  •  
  • cmd = (uint32_t)mIn.readInt32();

  • switch (cmd) {

  • case BR_TRANSACTION_COMPLETE:

  • if (!reply && !acquireResult) goto finish;

  • break;

  • ...

  • case BR_REPLY:

  • ...

  • goto finish;

  •  
  • ...

  • }

  •  
  • finish:

  • if (err != NO_ERROR) {

  • if (acquireResult) *acquireResult = err;

  • if (reply) reply->setError(err);

  • mLastError = err;

  • }

  •  
  • return err;

  • }

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

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

Binder中的生产者与消费者

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

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

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

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

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

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

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • void IPCThreadState::joinThreadPool(bool isMain)

  • {

  • mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

  •  
  • set_sched_policy(mMyThreadId, SP_FOREGROUND);

  •  
  • status_t result;

  • // 在这里进入循环,循环处理该进程服务的请求

  • do {

  • processPendingDerefs();

  • result = getAndExecuteCommand();

  • } while (result != -ECONNREFUSED && result != -EBADF);

  •  
  • mOut.writeInt32(BC_EXIT_LOOPER);

  • talkWithDriver(false);

  • }

  •  
  • status_t IPCThreadState::getAndExecuteCommand()

  • {

  • status_t result;

  • int32_t cmd;

  •  
  • // 请求driver,查看是否有需要处理的事务

  • result = talkWithDriver();

  • if (result >= NO_ERROR) {

  • ...

  • result = executeCommand(cmd);

  • }

  •  
  • return result;

  • }

  •  
  • status_t IPCThreadState::talkWithDriver(bool doReceive)

  • {

  • ...

  • // talkWithDriver的核心在于调用ioctl与内核进行通信

  • // 最终会在内核中调用binder_thread_read方法

  • // 如果没有获取到需要处理的事务,线程会处于阻塞状态

  • if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

  • err = NO_ERROR;

  • else

  • err = -errno;

  • ...

  • // 通信返回后,说明获取到了需要处理的事务,然后会bwr进行处理

  • }

梳理一下思路:

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

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

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

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

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

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

 
  • static int binder_wait_for_work(struct binder_thread *thread,

  • bool do_proc_work)

  • {

  • ...

  • for (;;) {

  • // 调用该方法使进程在等待队列上睡眠

  • prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);

  • // 当进程被唤醒后,检查todo队列是否不为空,如果是,则唤醒进程

  • if (binder_has_work_ilocked(thread, do_proc_work))

  • break;

  • ...

  • // 否则的话,将该线程的调度权交给调度器,再次进入睡眠

  • schedule();

  • ..

  • }

  • ...

  • }

典型的生产者逻辑:

 
  • ...

  • // 将事务插入到todo队列中

  • binder_enqueue_work_ilocked(&t->work, &target_thread->todo);

  • ...

  • // 随后唤醒等待队列

  • wake_up_interruptible_sync(&target_thread->wait);

  • ...

Binder代理对象的handle句柄

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

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

frameworks/native/libs/binder/BpBinder.cpp

 
  • status_t BpBinder::transact(

  • uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

  • {

  • // Once a binder has died, it will never come back to life.

  • if (mAlive) {

  • // 与内核通信

  • status_t status = IPCThreadState::self()->transact(

  • mHandle, code, data, reply, flags);

  • if (status == DEAD_OBJECT) mAlive = 0;

  • return status;

  • }

  •  
  • return DEAD_OBJECT;

  • }

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

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

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

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • status_t IPCThreadState::transact(int32_t handle,

  • uint32_t code, const Parcel& data,

  • Parcel* reply, uint32_t flags)

  • {

  • ...

  • if (err == NO_ERROR) {

  • // 将handle写入到binder_transaction_data中

  • err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

  • }

  • ...

  • if ((flags & TF_ONE_WAY) == 0) {

  • ...

  • // 最终还是通过调用waitForResponse来实现通信

  • if (reply) {

  • err = waitForResponse(reply);

  • } else {

  • Parcel fakeReply;

  • err = waitForResponse(&fakeReply);

  • }

  • ...

  • } else {

  • err = waitForResponse(NULL, NULL);

  • }

  • ...

  • }

  •  
  • status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,

  • int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)

  • {

  • binder_transaction_data tr;

  • tr.target.handle = handle;

  • ...

  • mOut.writeInt32(cmd);

  • // 特殊留意一下,binder_transaction_data最终被包装到Parcel类型的mOut中

  • mOut.write(&tr, sizeof(tr));

  •  
  • return NO_ERROR;

  • }

  •  
  • status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

  • {

  • uint32_t cmd;

  • int32_t err;

  •  
  • while (1) {

  • if ((err=talkWithDriver()) < NO_ERROR) break;

  • ...

  • }

  • }

  •  
  • status_t IPCThreadState::talkWithDriver(bool doReceive)

  • {

  • binder_write_read bwr;

  • ...

  • bwr.write_size = outAvail;

  • // 获取Parcel的数据,封装到binder_write_read中

  • bwr.write_buffer = (uintptr_t)mOut.data();

  • ...

  • // 最终将binder_write_read发送到内核,其中handle数据就在其中

  • if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

  • err = NO_ERROR;

  • else

  • err = -errno;

  • ...

  • }

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

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

 
  • static void binder_transaction(struct binder_proc *proc,

  • struct binder_thread *thread,

  • struct binder_transaction_data *tr, int reply,

  • binder_size_t extra_buffers_size)

  • {

  • ...

  • // 通过使用binder_proc->refs_by_desc红黑树与目前binder代理对比,获取都对应的binder_ref

  • ref = binder_get_ref_olocked(proc, tr->target.handle,

  • true);

  • if (ref) {

  • // 随后获取到binder_ref对应的binder_node,核心获取操作是调用ref->node

  • target_node = binder_get_node_refs_for_txn(

  • ref->node, &target_proc,

  • &return_error);

  • }

  • ...

  • }

  •  
  • static struct binder_node *binder_get_node_refs_for_txn(

  • struct binder_node *node,

  • struct binder_proc **procp,

  • uint32_t *error)

  • {

  • struct binder_node *target_node = NULL;

  •  
  • binder_node_inner_lock(node);

  • if (node->proc) {

  • target_node = node;

  • ...

  • // 通过node获取target_proc

  • *procp = node->proc;

  • } else

  • *error = BR_DEAD_REPLY;

  • binder_node_inner_unlock(node);

  •  
  • return target_node;

  • }

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

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

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

 
  • static void binder_transaction(struct binder_proc *proc,

  • struct binder_thread *thread,

  • struct binder_transaction_data *tr, int reply,

  • binder_size_t extra_buffers_size)

  • {

  • ...

  • // 获取发起调用时,被保存的stack信息

  • in_reply_to = thread->transaction_stack;

  •  
  • // 从stack中获取发起调用的线程,也就是返回操作的目标线程

  • target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);

  • ...

  • }

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

Binder内核中的红黑树

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

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

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

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

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

 
  • public void setSystemProcess() {

  • ...

  • ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);

  • ...

  • }

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

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

 
  • public static void addService(String name, IBinder service) {

  • try {

  • getIServiceManager().addService(name, service, false);

  • } catch (RemoteException e) {

  • Log.e(TAG, "error in addService", e);

  • }

  • }

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

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

frameworks/native/libs/binder/IServiceManager.cpp

 
  • virtual status_t addService(const String16& name, const sp<IBinder>& service,

  • bool allowIsolated)

  • {

  • Parcel data, reply;

  • data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

  • data.writeString16(name);

  • data.writeStrongBinder(service);

  • data.writeInt32(allowIsolated ? 1 : 0);

  • // 获取BpBinder对象

  • status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

  • return err == NO_ERROR ? reply.readExceptionCode() : err;

  • }

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

frameworks/native/libs/binder/BpBinder.cpp

 
  • status_t BpBinder::transact(

  • uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

  • {

  • if (mAlive) {

  • // 还是通过IPCThreadState进行调用,这里的mHandle句柄为0

  • status_t status = IPCThreadState::self()->transact(

  • mHandle, code, data, reply, flags);

  • if (status == DEAD_OBJECT) mAlive = 0;

  • return status;

  • }

  •  
  • return DEAD_OBJECT;

  • }

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

frameworks/native/cmds/servicemanager/service_manager.c

 
  • int svcmgr_handler(struct binder_state *bs,

  • struct binder_transaction_data *txn,

  • struct binder_io *msg,

  • struct binder_io *reply)

  • {

  • uint16_t *s;

  • ...

  • switch(txn->code) {

  • case SVC_MGR_ADD_SERVICE:

  • s = bio_get_string16(msg, &len);

  • if (s == NULL) {

  • return -1;

  • }

  • handle = bio_get_ref(msg);

  • allow_isolated = bio_get_uint32(msg) ? 1 : 0;

  • // 进行服务添加

  • if (do_add_service(bs, s, len, handle, txn->sender_euid,

  • allow_isolated, txn->sender_pid))

  • return -1;

  • break;

  • }

  • ...

  • }

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

frameworks/native/cmds/servicemanager/service_manager.c

 
  • int svcmgr_handler(struct binder_state *bs,

  • struct binder_transaction_data *txn,

  • struct binder_io *msg,

  • struct binder_io *reply)

  • {

  • uint16_t *s;

  • ...

  • switch(txn->code) {

  • case SVC_MGR_CHECK_SERVICE:

  • s = bio_get_string16(msg, &len);

  • if (s == NULL) {

  • return -1;

  • }

  • // 进行服务查询

  • handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);

  • if (!handle)

  • break;

  • bio_put_ref(reply, handle);

  • return 0;

  • }

  • ...

  • }

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

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

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

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

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

 
  • public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException {

  • Parcel data = Parcel.obtain();

  • Parcel reply = Parcel.obtain();

  • data.writeInterfaceToken(IActivityManager.descriptor);

  • data.writeInt(maxNum);

  • data.writeInt(flags);

  • data.writeInt(userId);

  • // 这里的mRemote在其实是BinderProxy的实例,通过transact调用与binder内核进行通信

  • mRemote.transact(GET_RECENT_TASKS_TRANSACTION, data, reply, 0);

  • reply.readException();

  • final ParceledListSlice<ActivityManager.RecentTaskInfo> list = ParceledListSlice.CREATOR

  • .createFromParcel(reply);

  • data.recycle();

  • reply.recycle();

  • return list;

  • }

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

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • status_t IPCThreadState::transact(int32_t handle,

  • uint32_t code, const Parcel& data,

  • Parcel* reply, uint32_t flags)

  • {

  • ...

  • if (err == NO_ERROR) {

  • LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),

  • (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");

  • // 将数据进行封装

  • err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

  • }

  • ...

  •  
  • if ((flags & TF_ONE_WAY) == 0) {

  • // 非ONE_WAY通信,传入reply参数,意味着服务端回返回数据

  • if (reply) {

  • err = waitForResponse(reply);

  • } else {

  • Parcel fakeReply;

  • err = waitForResponse(&fakeReply);

  • }

  • }

  • ...

  • }

  •  
  • status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,

  • int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)

  • {

  • binder_transaction_data tr;

  • ...

  • const status_t err = data.errorCheck();

  • if (err == NO_ERROR) {

  • tr.data_size = data.ipcDataSize();

  • tr.data.ptr.buffer = data.ipcData();

  • tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);

  • tr.data.ptr.offsets = data.ipcObjects();

  • }

  • ...

  • // 将Parcel数据封装成binder_transaction_data,其中包装了命令与数据内容

  • mOut.writeInt32(cmd);

  • mOut.write(&tr, sizeof(tr));

  •  
  • return NO_ERROR;

  • }

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

 
  • status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

  • {

  • uint32_t cmd;

  • int32_t err;

  •  
  • while (1) {

  • if ((err=talkWithDriver()) < NO_ERROR) break;

  • ...

  • }

  • ...

  • }

  •  
  • status_t IPCThreadState::talkWithDriver(bool doReceive)

  • {

  • binder_write_read bwr;

  • ...

  • bwr.write_size = outAvail;

  • // 获取mOut中的数据

  • bwr.write_buffer = (uintptr_t)mOut.data();

  • ...

  • // 调用ioctl和内核通信,通信数据为binder_write_read

  • if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

  • err = NO_ERROR;

  • ...

  • }

先调用talkWithDriver与内核通信

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

 
  • static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

  • {

  • int ret;

  • // proc引用保存在file的private_data引用中

  • struct binder_proc *proc = filp->private_data;

  • ...

  • switch (cmd) {

  • // command为BINDER_WRITE_READ,进入该代码块

  • case BINDER_WRITE_READ:

  • ret = binder_ioctl_write_read(filp, cmd, arg, thread);

  • if (ret)

  • goto err;

  • break;

  • ...

  • }

  •  
  • static int binder_ioctl_write_read(struct file *filp,

  • unsigned int cmd, unsigned long arg,

  • struct binder_thread *thread)

  • {

  • struct binder_write_read bwr;

  • // 从用户空间中拷贝数据到内核空间

  • if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {

  • ret = -EFAULT;

  • goto out;

  • }

  • ...

  • if (bwr.write_size > 0) {

  • // write_size大于0时,调用该方法进行事务生产

  • ret = binder_thread_write(proc, thread,

  • bwr.write_buffer,

  • bwr.write_size,

  • &bwr.write_consumed);

  • ...

  • }

  • if (bwr.read_size > 0) {

  • // read_size大于0,调用该方法进行事务消费

  • ret = binder_thread_read(proc, thread, bwr.read_buffer,

  • bwr.read_size,

  • &bwr.read_consumed,

  • filp->f_flags & O_NONBLOCK);

  • ...

  • }

  • ...

  • // 将内核空间处理过的bwr拷贝到用户空间

  • if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {

  • ret = -EFAULT;

  • goto out;

  • }

  • ...

  • }

 
  • 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)

  • {

  • switch (cmd) {

  • ...

  • case BC_TRANSACTION:

  • case BC_REPLY: {

  • struct binder_transaction_data tr;

  •  
  • // 将封装好的数据拷贝到内核空间

  • if (copy_from_user(&tr, ptr, sizeof(tr)))

  • return -EFAULT;

  • ptr += sizeof(tr);

  • binder_transaction(proc, thread, &tr,

  • cmd == BC_REPLY, 0);

  • break;

  • }

  • ...

  • }

  • }

  •  
  • static void binder_transaction(struct *proc,

  • struct binder_thread *thread,

  • struct binder_transaction_data *tr, int reply,

  • binder_size_t extra_buffers_size)

  • {

  • struct binder_proc *target_proc = NULL;

  • struct binder_thread *target_thread = NULL;

  • ...

  • if (tr->target.handle) {

  • struct binder_ref *ref;

  •  
  • binder_proc_lock(proc);

  • // 使用handle获取到binder_ref

  • ref = binder_get_ref_olocked(proc, tr->target.handle, true);

  • if (ref) {

  • // 通过binder_ref获取binder_node与target_proc

  • target_node = binder_get_node_refs_for_txn(

  • ref->node, &target_proc, &return_error);

  • }

  • binder_proc_unlock(proc);

  • }

  • ...

  • // 注意这里binder_work的类型

  • t->work.type = BINDER_WORK_TRANSACTION;

  • ...

  • // 如果此次通信为非ONE_WAY则需要向目标进程发起请求并等待

  • if (!(t->flags & TF_ONE_WAY)) {

  • binder_inner_proc_lock(proc);

  • t->need_reply = 1;

  • t->from_parent = thread->transaction_stack;

  • // 将此次事务保存在线程的transaction_stack中

  • thread->transaction_stack = t;

  • binder_inner_proc_unlock(proc);

  • if (!binder_proc_transaction(t, target_proc, target_thread)) {

  • ...

  • goto err_dead_proc_or_thread;

  • }

  • }

  • }

  •  
  • static bool binder_proc_transaction(struct binder_transaction *t,

  • struct binder_proc *proc,

  • struct binder_thread *thread)

  • {

  • ...

  • // 在这里将binder_work入队,这样目标进程就可以解除阻塞,开始进行事务处理

  • binder_enqueue_work_ilocked(&t->work, target_list);

  • }

这里梳理一下发送binder请求的流程:

  • 调用BinderProxy JNI方法到native,封装需要传输的数据
  • 调用BpBinder.transact()方法,继续向下委托
  • 调用IPCThreadState.talkWithDriver,继续封装数据
  • 最终调用ioctl方法与内核进行通信
  • 内核空间中,调用binder_ioctl,分流命令
  • 通过handle找到binder_ref,最终找到binder_proc
  • 将类型为BINDER_WORK_TRANSACTION的binder_work入队到binder_proc->todo队列中

插入事务到todo队列中之后,接下来的分析就到了目前服务所在进程的空间下了:

 
  • static int binder_thread_read(struct binder_proc *proc,

  • struct binder_thread *thread,

  • binder_uintptr_t binder_buffer, size_t size,

  • binder_size_t *consumed, int non_block)

  • {

  • ...

  • while (1) {

  • uint32_t cmd;

  • ...

  • w = binder_dequeue_work_head_ilocked(list);

  • case BINDER_WORK_TRANSACTION: {

  • binder_inner_proc_unlock(proc);

  • // 通过container_of宏获取到binder_transaction实例

  • t = container_of(w, struct binder_transaction, work);

  • } break;

  •  
  • ...

  • if (t->buffer->target_node) {

  • // 获取到目标进程服务相关信息

  • struct binder_node *target_node = t->buffer->target_node;

  • struct binder_priority node_prio;

  •  
  • tr.target.ptr = target_node->ptr;

  • tr.cookie = target_node->cookie;

  • ...

  • // 留意一下这里返回给用户空间所在进程的命令

  • cmd = BR_TRANSACTION;

  • }

  • ...

  • // 将目标进程服务地址与所需处理的数据从内核空间传到用户空间

  • if (put_user(cmd, (uint32_t __user *)ptr)) {

  • ...

  • return -EFAULT;

  • }

  • ptr += sizeof(uint32_t);

  • if (copy_to_user(ptr, &tr, sizeof(tr))) {

  • ...

  • return -EFAULT;

  • }

  • }

  • }

上述代码运行在内核空间中,当执行完毕后回返回到用户空间,这里直接分析IPCThreadState.executeCommand方法:

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • status_t IPCThreadState::executeCommand(int32_t cmd)

  • {

  • switch ((uint32_t)cmd) {

  • ...

  • case BR_TRANSACTION:

  • {

  • ...

  • // 获取到目标服务所在地址

  • if (tr.target.ptr) {

  • // 调用transact方法,执行服务端方法调用

  • error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags);

  • }

  • ...

  • }

  • ...

  • }

  • }

tr.cookie会被转换为BBinder对象,它是服务端的实体对象

frameworks/native/libs/binder/Binder.cpp

 
  • status_t BBinder::transact(

  • uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

  • {

  • data.setDataPosition(0);

  •  
  • status_t err = NO_ERROR;

  • switch (code) {

  • ...

  • default:

  • // 调用该方法进行进一步的事务分发

  • err = onTransact(code, data, reply, flags);

  • break;

  • }

  • ...

  •  
  • return err;

  • }

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

 
  • @Override

  • public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

  • switch (code) {

  • ...

  • case GET_RECENT_TASKS_TRANSACTION: {

  • data.enforceInterface(IActivityManager.descriptor);

  • int maxNum = data.readInt();

  • int fl = data.readInt();

  • int userId = data.readInt();

  • // 最终执行服务方法

  • ParceledListSlice<ActivityManager.RecentTaskInfo> list = getRecentTasks(maxNum,

  • fl, userId);

  • reply.writeNoException();

  • // 将结果列表写入到Parcel实例reply中

  • list.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

  • return true;

  • }

  • ...

  • }

  • }

到这里,服务端的对应方法就执行完毕了,但是对于非ONE_WAY的方法,需要返回处理结果给发出该请求的进程

frameworks/native/libs/binder/IPCThreadState.cpp

 
  • status_t IPCThreadState::executeCommand(int32_t cmd)

  • {

  • switch ((uint32_t)cmd) {

  • ...

  • case BR_TRANSACTION:

  • {

  • ...

  • if ((tr.flags & TF_ONE_WAY) == 0) {

  • // 调用该方法返回结果,需要经过binder驱动的中转

  • sendReply(reply, 0);

  • }

  • ...

  • }

  • ...

  • }

  • }

  •  
  • status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)

  • {

  • status_t err;

  • status_t statusBuffer;

  • // 写入返回的数据到mOut成员中, command命令为BC_REPLY

  • err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);

  • if (err < NO_ERROR) return err;

  •  
  • // 调用该方法与binder通信,注意这个方法是ONE_WAY的

  • return waitForResponse(NULL, NULL);

  • }

返回的流程与发出请求的流程大致相同,主要区别在于cmd命令不同,这里直接看binder_transaction的相关实现:

 
  • static void binder_transaction(struct *proc,

  • struct binder_thread *thread,

  • struct binder_transaction_data *tr, int reply,

  • binder_size_t extra_buffers_size)

  • {

  • ...

  • if (reply) {

  • // 通过获取到与binder线程关联的事务获取到发起请求的线程

  • in_reply_to = thread->transaction_stack;

  • ...

  • target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);

  • }

  • ...

  • // 之后的操作同样是将binder_work插入到todo队列中,唤醒请求端进程进行处理

  • }

辅助功能:匿名服务的跨进程传输与回调

这一小节的原理与内容其实与上一节高度相似,这里单独分析的原因是Android跨进程通信中,对于匿名服务的使用十分广泛,但是这种写法往往又十分另新手困惑。例如常见的四大组件,无不与匿名服务紧密关联,这里以动态广播注册要讲解匿名服务的传输与回调

 
  • public Intent registerReceiver(IApplicationThread caller, String packageName,

  • IIntentReceiver receiver,

  • IntentFilter filter, String perm, int userId) throws RemoteException

  • {

  • // 这里IApplicationThread caller其实也是匿名服务,这里先忽略

  • Parcel data = Parcel.obtain();

  • Parcel reply = Parcel.obtain();

  • ...

  • // receiver为一个动态创建的匿名服务IIntentReceiver实例

  • data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);

  • ...

  • mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);

  • reply.readException();

  • ...

  • reply.recycle();

  • data.recycle();

  • return intent;

  • }

receiver实例将回在writeStrongBinder中被封装,随后传输到binder驱动,驱动在中转期间会取出binder对象并进行实体对象、引用对象的创建,最终会将引用对象传递到binder服务端,服务端只需要将它进行保存,就可以随时进行调用以实现回调

frameworks/native/libs/binder/Parcel.cpp

 
  • status_t Parcel::writeStrongBinder(const sp<IBinder>& val)

  • {

  • return flatten_binder(ProcessState::self(), val, this);

  • }

  •  
  • status_t flatten_binder(const sp<ProcessState>& /*proc*/,

  • const sp<IBinder>& binder, Parcel* out)

  • {

  • // 封装的对象

  • flat_binder_object obj;

  •  
  • obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;

  • if (binder != NULL) {

  • IBinder *local = binder->localBinder();

  • if (!local) {

  • ...

  • } else {

  • // 当前为本地服务对象,将会记录地址并将类型设置为BINDER_TYPE_BINDER

  • obj.type = BINDER_TYPE_BINDER;

  • obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());

  • obj.cookie = reinterpret_cast<uintptr_t>(local);

  • }

  • }

  •  
  • return finish_flatten_binder(binder, obj, out);

  • }

中间的分析和之前一致,不再冗余了,直接到binder_transaction函数中

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

 
  • static void binder_transaction(struct *proc,

  • struct binder_thread *thread,

  • struct binder_transaction_data *tr, int reply,

  • binder_size_t extra_buffers_size)

  • {

  • ...

  • // 传输过程中会涉及binder对象的检查,如果存在则需要进行解析

  • hdr = (struct binder_object_header *)(t->buffer->data + *offp);

  • off_min = *offp + object_size;

  • switch (hdr->type) {

  • case BINDER_TYPE_HANDLE:

  • case BINDER_TYPE_WEAK_HANDLE: {

  • struct flat_binder_object *fp;

  •  
  • fp = to_flat_binder_object(hdr);

  • ret = binder_translate_handle(fp, t, thread);

  • if (ret < 0) {

  • return_error = BR_FAILED_REPLY;

  • return_error_param = ret;

  • return_error_line = __LINE__;

  • goto err_translate_failed;

  • }

  • } break;

  • }

  • ...

  • }

  •  
  • static int binder_translate_binder(struct flat_binder_object *fp,

  • struct binder_transaction *t,

  • struct binder_thread *thread)

  • {

  • // 如果实体对象之前并未创建,则会进行创建操作

  • node = binder_get_node(proc, fp->binder);

  • if (!node) {

  • node = binder_new_node(proc, fp);

  • if (!node)

  • return -ENOMEM;

  • }

  • ...

  • // 接下来的操作将会增加实体对象的引用

  • }

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

 
  • @Override

  • public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

  • switch (code) {

  • ...

  • case REGISTER_RECEIVER_TRANSACTION:

  • {

  • data.enforceInterface(IActivityManager.descriptor);

  • ...

  • // 调用到AMS接收端,回调用readStrongBinder进行binder对象的解析

  • b = data.readStrongBinder();

  • IIntentReceiver rec

  • = b != null ? IIntentReceiver.Stub.asInterface(b) : null;

  • ...

  • Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);

  • ...

  • return true;

  • }

  • ...

  • }

  • }

readStrongBinder本质上是调用了Parcel.java中的JNI方法

frameworks/base/core/jni/android_os_Parcel.cpp

 
  • static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)

  • {

  • Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);

  • if (parcel != NULL) {

  • // 通过该核心方法,从内核中读取出binder对象

  • return javaObjectForIBinder(env, parcel->readStrongBinder());

  • }

  • return NULL;

  • }

frameworks/native/libs/binder/Parcel.cpp

 
  • sp<IBinder> Parcel::readStrongBinder() const

  • {

  • sp<IBinder> val;

  • readStrongBinder(&val);

  • return val;

  • }

  •  
  • status_t Parcel::readStrongBinder(sp<IBinder>* val) const

  • {

  • return unflatten_binder(ProcessState::self(), *this, val);

  • }

  •  
  • status_t unflatten_binder(const sp<ProcessState>& proc,

  • const Parcel& in, sp<IBinder>* out)

  • {

  • const flat_binder_object* flat = in.readObject(false);

  •  
  • if (flat) {

  • switch (flat->type) {

  • case BINDER_TYPE_BINDER:

  • ...

  • case BINDER_TYPE_HANDLE:

  • // 因为这里处于进行读取端的进程,所以这时类型为HANDLE

  • // 同理通过传入过来的句柄来创建BpBinder对象

  • // out也是将回返回给javaObjectForIBinder进一步处理的引用对象

  • *out = proc->getStrongProxyForHandle(flat->handle);

  • return finish_unflatten_binder(

  • static_cast<BpBinder*>(out->get()), *flat, in);

  • }

  • }

  • return BAD_TYPE;

  • }

最终会在native中创建java层代理对象

frameworks/base/core/jni/android_util_Binder.cpp

 
  • jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)

  • {

  • ...

  • object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);

  • ...

  • }

返回到java层,会再调用IIntentReceiver.Stub.asInterface(b)方法对BinderProxy进行深一步封装,最终AMS会对IBinder对象进行存储并作为主要数据结构的键,方便客户端进行索引。当需要回调到客户端进程时,可以随时使用传入的代理对象进行实现

辅助功能:死亡回调的注册与获取

死亡回调是binder架构中极其重要的一个功能,它的意义在于,当服务端进行死亡后,可以告知各个客户端进程、进行一些反注册的注销操作。死亡回调的核心实现原理在于,当服务端进程死亡后,内核会对它之前打开过的/dev/binder文件进行关闭,随后binder内核会收到调用,并对注册了死亡监听的客户端进程们进行进一步的回调

以下从java层死亡回调的注册开始分析:

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

 
  • try {

  • AppDeathRecipient adr = new AppDeathRecipient(

  • app, pid, thread);

  • // 获取BinderProxy进行死亡回调的注册

  • thread.asBinder().linkToDeath(adr, 0);

  • app.deathRecipient = adr;

  • } catch (RemoteException e) {

  • ...

  • // 注册异常

  • }

BinderProxy的linkToDeath与transact一样,委托给了native层进行实现,最终会调用到BpBinder

frameworks/native/libs/binder/BpBinder.cpp

 
  • status_t BpBinder::linkToDeath(

  • const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)

  • {

  • Obituary ob;

  • ob.recipient = recipient;

  • ob.cookie = cookie;

  • ob.flags = flags;

  •  
  • {

  • AutoMutex _l(mLock);

  •  
  • if (!mObitsSent) {

  • if (!mObituaries) {

  • // 进程可以注册单个binder服务的多个死亡回调

  • mObituaries = new Vector<Obituary>;

  • if (!mObituaries) {

  • return NO_MEMORY;

  • }

  • getWeakRefs()->incWeak(this);

  • IPCThreadState* self = IPCThreadState::self();

  • // 调用该方法进行注册

  • self->requestDeathNotification(mHandle, this);

  • // 执行与binder驱动的通信

  • self->flushCommands();

  • }

  • ssize_t res = mObituaries->add(ob);

  • return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;

  • }

  • }

  •  
  • return DEAD_OBJECT;

  • }

msm-3.18/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)

  • {

  • ...

  • switch (cmd) {

  • case BC_REQUEST_DEATH_NOTIFICATION:

  • case BC_CLEAR_DEATH_NOTIFICATION: {

  • // 在内核中创建对应的binder_ref_death

  • death = kzalloc(sizeof(*death), GFP_KERNEL);

  • ...

  • // 引用到binder_ref中进行保存

  • ref->death = death;

  • }

  • }

  • }

至此,注册操作大致完成,接下来继续看死亡回调是如何从内核调用到客户端的

/dev/binder文件被关闭后,会调用实现注册的binder_release方法

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

 
  • static int binder_release(struct inode *nodp, struct file *filp)

  • {

  • struct binder_proc *proc = filp->private_data;

  •  
  • // 进行进程相关资源销毁,之后会调用到实体对象的资源释放

  • binder_defer_work(proc, BINDER_DEFERRED_RELEASE);

  •  
  • return 0;

  • }

  •  
  • static int binder_node_release(struct binder_node *node, int refs)

  • {

  • ...

  • // 遍历每个实体对象的引用对象

  • hlist_for_each_entry(ref, &node->refs, node_entry) {

  • refs++;

  •  
  • binder_inner_proc_lock(ref->proc);

  • if (!ref->death) {

  • binder_inner_proc_unlock(ref->proc);

  • continue;

  • }

  •  
  • death++;

  •  
  • BUG_ON(!list_empty(&ref->death->work.entry));

  • // 将binder_work类型置为BINDER_WORK_DEAD_BINDER

  • ref->death->work.type = BINDER_WORK_DEAD_BINDER;

  • // 将binder_work放入到binder_ref所在进程的todo队列中

  • binder_enqueue_work_ilocked(&ref->death->work,

  • &ref->proc->todo);

  • binder_wakeup_proc_ilocked(ref->proc);

  • binder_inner_proc_unlock(ref->proc);

  • }

  • ...

  • }

读取的操作与之前的数据传输操作是类似的,都是在binder_thread_read中进行的

 
  • static int binder_thread_read(struct binder_proc *proc,

  • struct binder_thread *thread,

  • binder_uintptr_t binder_buffer, size_t size,

  • binder_size_t *consumed, int non_block)

  • {

  • ...

  • switch (w->type) {

  • case BINDER_WORK_DEAD_BINDER:

  • case BINDER_WORK_DEAD_BINDER_AND_CLEAR:

  • case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {

  • ...

  • // 获取到binder_ref_death对象

  • death = container_of(w, struct binder_ref_death, work);

  • ...

  • // 之后回到用户空间,进行回调操作

  • }

  • }

  • ...

  • }

可以看到binder的死亡调用的代码复杂度其实比核心功能数据传输还是高一些,但是这两者的设计原理其实大体一致,一旦掌握了核心套路,代码的流转就很好把握

附:内核基础知识

链表: list_head

msm-3.18/include/linux/types.h

 
  • struct list_head {

  • struct list_head *next, *prev;

  • };

双向链表是最常见的数据结构之一,在binder驱动中用以实现todo队列

方法名作用
INIT_LIST_HEAD初始化链表
list_add插入节点
list_del删除节点
list_replace替换节点
list_empty链表是否为空

通过判断todo链表队列是否为空,来决定是否在binder_thread_read中唤醒进程:

 
  • static bool binder_has_work_ilocked(struct binder_thread *thread,

  • bool do_proc_work)

  • {

  • return !binder_worklist_empty_ilocked(&thread->todo) ||

  • thread->looper_need_return ||

  • (do_proc_work &&

  • !binder_worklist_empty_ilocked(&thread->proc->todo));

  • }

散列表: hlist_head

msm-3.18/include/linux/types.h

 
  • struct hlist_head {

  • struct hlist_node *first;

  • };

  •  
  • struct hlist_node {

  • struct hlist_node *next, **pprev;

  • };

与list_head双指针表头双链表的设计相比,hlist为单指针表头双链表,hlist_head内部仅有一个指针指向第一个节点,该结构适于用在HASH表中,在链表泛滥的HASH表中,相比list_head节省了许多空间损耗

典型遍历过程:

 
  • hlist_for_each_entry(itr, &binder_procs, proc_node) {

  • if (itr->pid == pid) {

  • seq_puts(m, "binder proc state:\n");

  • print_binder_proc(m, itr, 1);

  • }

  • }

红黑树: rb_root

msm-3.18/include/linux/rbtree.h

 
  • struct rb_node {

  • unsigned long __rb_parent_color; // 父节点的颜色

  • struct rb_node *rb_right;

  • struct rb_node *rb_left;

  • } __attribute__((aligned(sizeof(long))));

  •  
  • struct rb_root {

  • struct rb_node *rb_node;

  • };

红黑树的特性:

  1. 每个节点要么是黑色,要么是红色
  2. 根节点是黑色
  3. 每个为空的叶子节点是黑色
  4. 如果一个节点为红色,那么它的子节点必须是黑色
  5. 从一个节点到该节点的子孙节点的所有路径上,包含相同数目的黑节点

由以上特性可以证明出,红黑树的高度至多为: 2*log(n+1)

依我的理解,红黑树的查找效率虽然没有AVL树高,但是其插入、删除效率更高,是AVL树于BST实现的折中数据结构

核心方法:

方法名作用时间复杂度
rb_insert_color插入节点O(1)
rb_erase删除节点O(1)
rb_entry获取持有节点的实例O(1)
rb_first获取root节点O(1)
rb_last获取下一个节点O(1)
rb_next获取上一个节点O(1)

典型遍历过程:

 
  • for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {

  • ref = rb_entry(n, struct binder_ref, rb_node_desc);

  • if (ref->data.desc > new_ref->data.desc)

  • break;

  • new_ref->data.desc = ref->data.desc + 1;

  • }

典型查找过程:

 
  • p = &proc->refs_by_desc.rb_node; // 获取到红黑树root节点

  • while (*p) {

  • parent = *p;

  • // 原理是使用了container_of宏,通过红黑树节点,获取持有该节点的binder_ref实例

  • ref = rb_entry(parent, struct binder_ref, rb_node_desc);

  •  
  • // 查询过程

  • if (new_ref->data.desc < ref->data.desc)

  • p = &(*p)->rb_left;

  • else if (new_ref->data.desc > ref->data.desc)

  • p = &(*p)->rb_right;

  • else

  • BUG();

  • }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值