}
return 0;
}
在Binder驱动中,通过binder_procs集合记录了所有使用Binder的进程。每个初次打开Binder设备的进程都会创建一个binder_proc结构体对象,用来描述使用Binder的进程,然后被添加到这个列表中的。
在打开Binder设备之后,上层进程就要通过mmap进行内存映射。mmap的作用有如下两个:
-
申请一块内存空间,用来接收Binder通信过程中的数据
-
对这块内存进行地址映射,以便将来访问
mmap在内核对应的就是binder_mmap()函数:在这个函数中,会申请一块物理内存,然后将用户空间和内核空间虚拟地址同时对应到这块物理内存上。在这之后,当有Client要发送数据给Server的时候,只需一次copy_from_user动作,将Client发送过来的数据拷贝到Server端的内核空间指定的内存地址即可(从而实现前文说的Binder通信机制只需要一次内存拷贝的效果),由于这个内存地址在服务端已经同时映射到用户空间,因此无需再做一次复制,Server即可直接访问,整个过程如下图所示:
这幅图的说明如下:
-
Server在启动之后,调用对/dev/binder设备调用mmap;
-
内核中的binder_mmap函数进行对应的处理:申请一块物理内存,然后在用户空间和内核空间同时进行映射;
-
Client通过BINDER_WRITE_READ命令发送请求,这个请求将先到驱动中,同时需要将数据从Client进程的用户空间拷贝到内核空间;
-
驱动通过BR_TRANSACTION通知Server有人发出请求,Server进行处理。由于这块内存也在用户空间进行了映射,因此Server进程的代码可以直接访问。
3.4 Binder驱动控制协议——binder_ioctl
binder_ioctl()实现了上层应用进程与Binder驱动之间的交互命令,可以说承载了Binder驱动的大部分业务,也是我们学习的重中之重。下面表格中列出binder_ioctl支持的命令:
| 命令 | 说明 |
| — | — |
| BINDER_WRITE_READ | 读写操作,可以用此命令向Binder读取或写入数据 |
| BINDER_SET_MAX_THREADS | 设置支持的最大线程数。因为客户端可以并发向服务器端发送请求,如果Binder驱动发现当前线程数已经超过设定值,就会告知Binder Server停止启动新的线程 |
| BINDER_SET_CONTEXT_MGR | Service Manager专用,将自己设置为“Binder大管家”。系统中只能有一个SM存在 |
| BINDER_THREAD_EXIT | 通知Binder线程退出。每个线程退出时都应该告知Binder驱动、才能释放相关资源;否则会造成内存泄漏 |
| BINDER_VERSION | 获取Binder版本号 |
其中BINDER_WRITE_READ这个命令是重点,又分为若干子命令,如下表所示:
| 命令 | 说明 |
| — | — |
| BC_TRANSACTION | Binder事务,即:Client对于Server的请求 |
| BC_REPLY | 事务的应答,即:Server对于Client的回复 |
| BC_ENTER_LOOPER | 通知驱动主线程ready |
| BC_REGISTER_LOOPER | 通知驱动子线程ready |
| BR_REPLY | 通知进程收到Binder请求的回复(Client) |
| BR_TRANSACTION_COMPLETE | 驱动对于接受请求的确认回复 |
| BR_TRANSACTION | 通知进程收到一次Binder请求(Server端) |
| BR_DEAD_BINDER | 发送死亡通知 |
| BR_SPAWN_LOOPER | 通知Binder进程创建一个新的线程 |
其中BC_TRANSACTION和BC_REPLAY是最关键的两个命令,Binder机制中Client与Server交互基本靠它们完成。
单独看上面的协议可能很难理解,这里我们以一次Binder请求过程来详细看一下Binder协议是如何通信的,就比较好理解了。
这幅图的说明如下:
-
Binder是C/S架构的,通信过程牵涉到:Client,Server以及Binder驱动三个角色
-
Client对于Server的请求以及Server对于Client回复都需要通过Binder驱动来中转数据
-
BC_XXX命令是进程发送给驱动的命令
-
BR_XXX命令是驱动发送给进程的命令
-
整个通信过程由Binder驱动控制
=====================================================================================
Binder Framework的C++部分:主要功能是实现向下与Binder驱动的对接交互,并封装复杂的内部实现,对外提供使用接口。头文件定义位于:/frameworks/native/include/binder/,实现位于这个路径:/frameworks/native/libs/binder/ 。Binder库最终会编译成一个动态链接库libbinder.so,供其他进程链接使用。为了便于说明,下文中我们将Binder Framework 的C++部分称为libbinder。
先用一张类图描述libbinder中的主要类结构之间的关系:
对照上面这张libbinder的设计类图,我们来理一下各个核心类的功能与职责:
- 基类
-
IBinder:Binder对象的基类,这个类描述了所有在Binder上传递的对象,它既是Binder服务端对象BBinder的父类,也是Binder客户端对象BpBinder的父类;主要定义的方法有:a.transact ———进行一次Binder操作;b.queryLocalInterface——尝试获取本地Binder对象;c.getInterfaceDescriptor ——获取Binder的服务接口唯一的描述;d.isBinderAlive——查询Binder服务是否还活着等;
-
IInterface:Binder服务接口的基类,Binder服务通常需要同时提供客户端接口和服务端接口。每个Binder服务都是为了某个功能而实现的,因此其本身会定义一套接口集来描述自己提供的所有功能。而Binder服务既有自身实现服务的类,也要有给客户端进程调用的类。为了便于开发,这两中类里面的服务接口应当是一致的。因此为了实现方便,本地实现类和远程接口类需要有一个公共的描述服务接口的基类(即上图中的IXXXService)来继承。而这个基类通常是IInterface的子类。
- 客户端类
-
BpBinder :BpBinder的实例代表了客户端Binder,这个类的对象将被客户端调用。这个类最重要就是提供了transact方法,这个方法会将客户端调用的参数封装好通过IPCThreadState逻辑封装后发送给Binder驱动。
-
BpInterface:客户端接口的基类,远程接口是供客户端调用的接口集。BpInterface是个模板类,它们在继承自INTERFACE的基础上还继承了BpRefBase,通过这个类的remote方法可以获取到指向服务实现方的句柄。
- 服务端类
-
BBinder:BBinder的实例代表了服务端Binder,它描述了服务的提供方,所有Binder服务的实现者都要继承这个类(的子类),在继承类中,最重要的就是实现onTransact方法,因为这个方法是所有请求的入口。因此,这个方法是和BpBinder中的transact方法对应的,这个方法同样也有一个uint32_t code参数(统一在IBinder中定义),在这个方法的实现中,由服务提供者通过code对请求的接口进行区分,然后调用具体实现服务的方法。
-
BnInterface:服务端接口的基类,是需要服务端服务中真正实现的接口集。BnInterface是个模板类,它们在继承自INTERFACE(Binder服务接口的基类,继承自IInterface)的基础上还继承了BBinder,由此可以通过复写onTransact方法来提供实现。
- 与驱动通信的类
-
ProcessState : 代表使用Binder的进程。在讲解Binder驱动的时候我们就提到:任何使用Binder机制的进程都必须要对/dev/binder设备进行open以及mmap之后才能使用,这部分逻辑是所有使用Binder机制进程共同的。对于这种共同逻辑的封装便是Framework层的职责之一。libbinder中,ProcessState类封装了这个逻辑,从而负责进程Binder的初始化。
- <