Binder是Android中使用最为广泛的IPC框架,从实现的角度可以分为内核层和用户空间层,本文主要分析下Binder Driver在内核层的实现。

Binder Driver初始化

我们看下binder_init

misc_register方法向kernel注册了一个Misc设备,即虚拟设备节点,注册以后,binder driver就会成为内核驱动,我们应用层的系统调用如read, write, mmap系统调用,都会转到这个binder driver中进行。

具体的注册过程是根据binder_miscdev结构体进行的

可以看出,主要还是提供了open,mmap,flush等系统调用。

我们接下来会逐一分析。

binder_open分析

binder_open是映射的open函数,我们看下上层是什么时候调用open()的。

Binder Service端在初始化的时候,会创键一个ProcessState对象,这个是进程内的单例。

self的过程就是去获取一个进程内的单例。

看下构造过程

可以看到,在构造器中有open_driver()这个调用,作用就是打开binder_driver。

我们看到,ProcessState使用open_driver打开了binder设备,并且执行了比较binder驱动版本,设置最大线程个数的操作。

用户空间的open()会直接调用到kernel的binder_open()上去。

binder_open函数的主要逻辑是创建一个binder_proc结构体,并将binder_proc结构体放在binder driver的全局链表中和flip的private_data中。

binder_proc结构

binder_proc用来描述一个正在使用Binder的进程,如果该进程需要使用Binder进行通信,那么在开始使用前,系统会为该线程创建binder_proc实例。 定义如下:

binder_open就分析到这里了,核心逻辑是用户空间从ProcessState中进行打开binder节点,然后内核对该进程封装一个binder_proc结构存放在自己的节点中。

binder_mmap分析

binder_mmap是binder内存管理的核心方法,我们具体看下逻辑。

先从调用点开始分析,用户空间对mmap的调用是在ProcessState构造的时候。

构造器中,调用了mmap并将返回的用户空间地址存放在mVMStart中。

我们看下kernel中binder_mmap实现。

可以看出,binder_mmap使用get_vm_area分配了(最多)4MB的虚拟内存空间(此时并没有分配物理内存页面)。 然后使用binder_proc中的buffer字段记录该虚拟内存的地址,使用user_buffer_offset来记录用户空间地址与内核空间地址的差值。

这个user_buffer_offset是比较关键的逻辑,在IPC过程中,我们直接通过计算从用户空间读取内核中的值,免于再一次拷贝。

分配完虚拟空间地址后,会通过kzalloc分配物理内存页面,然后通过binder_update_page_range将物理页面和虚拟地址空间进行关联。

然后使用binder_buffer描述该内存结构,并将binder_buffer放入binder_proc的列表buffers中。

binder_ioctl分析

binder_ioctl是binder driver的核心机制,binder没有提供readwrite之类的系统调用,而是直接使用ioctl进行操作。

进行了解binder_ioctl之前,我们先看下binder协议码,这个也是ioctl的参数之一。

binder.h中一共定义了多个ioctl的状态码。其中Client发起的协议码以BC开头,Server或者Driver发起的协议码以BR开头。

我们看下binder_ioctl的实现。

我们以BINDER_WRITE_READ为例进行分析。 在IPCThreadStatetalkWithDriver中,使用了该协议码。

该协议会传递到内核一个binder_write_read结构体。我们看下该结构体的定义。

这个结构体在binder.h中,在内核和用户空间都有相同的定义。 binder_write_read里面有write_bufferread_buffer,这两个分别是读写缓冲区的指针。

我们看下这个case的逻辑。

在这个case之前, driver会首先使用binder_get_thread找到当前进行binder call的线程,具体过程就不详细说了,是根据内核线程pid进行查找,没有记录的话,会重新创建一个binder_thread结构,然后放在binder_procthreads红黑树中。

接着看,如果write_size>0那么走binder_thread_write逻辑,如果read_size>0那么走binder_thread_read逻辑,完了有会调用copy_to_user把这个结构体拷贝回用户空间。

我们先依次分析,先看binder_thread_write实现。

我们先来看下这个cmd是何时写入的。 在IPCThreadStatetransact中,会把BC_TRANSACTION作为cmd码进行写入。

可以看出,实际上是写入mOut中了,在binder_thread_write中通过偏移计算得出这个值。

我们看下这个BC_TRANSACTION这个case

看来有个binder_transaction_data结构体啊,我们看下这个结构体定义,用户空间和内核空间共同使用一份定义。

这个binder_transaction_data是在IPCThreadStatetransact时写入mOut中的。 里面存放了当前IPC所需要的数据。 继续我们的逻辑,内核会用copy_from_user从用户空间把binder_transaction_data结构体拷贝过来。 然后调用binder_transaction

binder_transaction分析

这个函数非常的长,推荐装个代码折叠插件看。 我们先看下查找目标进程binder_ref的过程。binder的引用对象在内核中的描述为binder_ref结构。

获取binder_ref的过程

通过binder_get_ref根据handle值获取binder_ref,然后获取对端的binder_proc,并记录在target_node节点中。

然后获取目标线程的todo队列和wait队列,并记录在变量中。

接下来我们会看到一次内存拷贝过程,又原进程的用户空间拷贝到内核,这是binder IPC中完成的一次至关重要的拷贝,binder机制的一次内存拷贝说的就是这个地方。

然后binder内核把一个BINDER_WORK_TRANSACTION的工作添加到目标进程todo队列,把BINDER_WORK_TRANSACTION_COMPLETE添加到原线程的todo队列中。

然后唤醒目标进程,开始处理todo队列里的工作。

BINDER_WORK_TRANSACTION_COMPLETE这个是binder内部的work type,最终还是会转成BC_XXX或者BR_XXX之类的协议。

当其他进程往todo队列中放入binder_work时,当前进程会被唤醒,执行todo队列中的工作,我们接下来看下binder_thread_read的相关逻辑。

binder_thread_read分析

Binder进程最终是在binder_thread_read中进行阻塞的,因为BnBinder会调用joinThreadPool一直询问Binder驱动是否存在Binder请求,如果没有的话,会一直阻塞住,上文分析到,其他进程会往目标进程的todo队列中放入binder_work使目标线程从binder_thread_read的逻辑中进行唤醒。

我们就看下BINDER_WORK_TRANSACTION的逻辑吧,其他逻辑类似。

这段逻辑就是把处理后的binder_transaction_data结构返回给用户空间,这个时候,用户层就会调用BnBinder的相关逻辑进行处理了。

总结下Binder协议通信流程

最后,我们从协议的角度分析下Binder Driver处理IPC的流程图。

下面根据时序编号解释下:

(1) Client向Binder Driver发送请求,此时的CMD是上层talkWithDriver之前传入的BC_TRANSACTION

(2,3) Binder Driver在binder_transaction函数中处理此逻辑,通知Client处理BR_TRANSACTION_COMPLETE,意在告诉Client内核已收到请求,同时向目标Server发送BR_TRANSACTION,把Server端唤醒。

(4) Server执行BnBinder的逻辑后,用户层sendReply写入BC_REPLY,通知Binder Driver处理。

(5) 同样Binder Driver会回复一个BR_TRANSACTION_COMPLETE通知内核已收到请求。

(6) 收到BC_REPLY后,Binder Driver会向Client发送BR_REPLY,通知Server端已经返回,执行BpBinder端逻辑。

到此Binder Driver的核心逻辑就分析完了,接下来会分析Binder在用户空间层的实现,会用到这些知识。

参考资料

http://gityuan.com/2015/11/02/binder-driver-2/

https://juejin.im/post/58c90816a22b9d006413f624#heading-2

https://juejin.im/post/58c91e81570c35005889b5a6

https://juejin.im/post/58c920ba61ff4b0060120855

https://github.com/RickAi/Binder_SourceCode

发表评论

电子邮件地址不会被公开。 必填项已用*标注