前文binder(一)Linux必备知识篇已经了解过一些Linux相关的知识。
因为Android是基于Linux内核的操作系统,所以Android也具备Linux进程隔离的特性。每个进程都是一块单独的内存单元,各个进程之间要互相通信需要一套IPC机制来实现。
Linux进程间通信
简单介绍一下Linux进程间通信方式。
linux中主要的进程通信包括以下几种:
通信方式 | 性能/拷贝 | 安全性 | 稳定性 | 特性 |
---|---|---|---|---|
信号(signal) | - | - | - | 事件通知,不适用于信息交换 |
信号量(Semaphore) | - | - | - | 计数器,常被用作锁机制 |
管道(pipe) | 低/2 | - | 1对1 | 单次只能单向传输 |
消息队列(Message queues) | 低/2 | - | - | - |
套接字(Socket) | 低/2 | 高 | C/S架构 | 常用于网络通信 |
共享内存(Share Memory) | 高/0 | 考虑并发 | 用户维护 | 使用较为复杂 |
以上是Linux进程间通信的几种主要方式,各种方式都有自己的优缺点,也有自己的擅长使用领域,可自行了解一下。
Android中的进程通信(Binder)
在Android中主要是采用Binder来实现的。当然并不是说Android中所有的IPC都是binder(例如SystemServer进程就是通过socket和zygote进程通信的)。
之所以选择Binder这种方式作为Android中的IPC方式主要是因为:
通信方式 | 性能/拷贝 | 安全性 | 稳定性 | 特性 |
---|---|---|---|---|
Binder | 中/1 | 高:进程UID标识,鉴别身份 | C/S架构 | 通过mmap()内存映射实现1次拷贝进行IPC,安全性高 |
Binder最早并不是Google提出的,它的前身是Be Inc公司开发的OpenBinder,后来OpenBinder的作者Dianne Hackborn加入了Google公司,并负责Android平台的开发工作,所以把这项技术也带进了Android。
在Android 8.0 之前,Binder机制比较简单,只有一个驱动设备"/dev/binder",一个守护进程"/system/bin/servicemanager",一个binder库"/system/lib64/libbinder.so"。
在Android 8.0开始,Android引入了Treble的机制,为了方便Android系统的快速移植、升级,提升系统稳定性,Binder机制被拓展成了"/dev/binder", “/dev/hwbinder”,“/dev/vndbinder”。
我们原先使用的"/dev/binder",成为框架进程的专有节点,这意味着供应商进程无法再访问此节点。供应商进程可以访问 /dev/hwbinder,但必须将其 AIDL 接口转为使用 HIDL。
对于想要继续在供应商进程之间使用 AIDL 接口的供应商,需要使用 /dev/vndbinder(而非 /dev/binder)。
Android8.0及之后的Binder域如下所示:
IPC域 | 说明 |
---|---|
/dev/binder | 框架/应用之间的IPC,使用AIDL接口 |
/dev/hwbinder | 框架/供应商之间的IPC和供应商进程间的IPC,使用HIDL接口 |
/dev/binder | 供应商/供应商之间的IPC,使用AIDL接口 |
从binder(一)Linux必备知识篇可知,Linux为了安全性将内存空间氛围用户空间和内核空间,并且由于进程隔离机制所以两个进程之间要进行通信必须借助内核空间来实现,而进程想借助内核空间进行通信就必须让cpu从用户态转入内核态,就得通过系统调用来实现。
使用binder作为IPC方式也是如此,binder与其他IPC方式的区别在于整个通信过程对于数据只需要一次拷贝即可,也就是图中的copy_from_user()这个系统调用实现的,进程B的用户空间缓存区与内核中的数据缓存区存在内存映射,也就是说使用了mmap()方法将这两个虚拟地址都指向了同一个物理内存从而省去了一次内核空间到用户空间的拷贝。
在Android系统中对binder的设计主要分为4个角色:Binder驱动、service_manager、Server端和Client端。
先用图片了解一下最最简单的binder设计框架。
service_manager进程
binder是一种C-S架构的IPC方式,而service_manager是以服务管理者的形式存在的。它是一个用户空间的独立进程,也是一个守护进程,在Android系统启动时加载完驱动之后,会启动Android的init程序,init程序会解析init.rc,进而启动init.rc中定义的守护进程。而service_manager正是通过注册在init.rc中而被启动的。
service_manager启动之后会被赋值给全局变量,因为Client和Server都需要和service_manager通信。
Android系统中的各个服务,都是注册到service_manager中进行管理的,而且每个服务都对应一个服务名。当Client获取某个服务时,则通过服务名来从service_manager中获取相应的服务。
Server进程
当某个进程要当成Server端提供某些能力时,它需要先将自己注册进service_manager,当service_manager收到注册请求之后就会将该Server的相关信息注册到其内部"Binder引用组成的单链表"中,这里所说的Server相关信息主要包含2部分:Server对应服务名和server对应的binder实体的一个binder引用。当然这整个流程需要binder驱动的参与,后面说。
Client进程
Client进程想要和某个Server假设是服务B进行通信,必须先获取到该Server的远程服务(先不用关心什么是远程服务),大概步骤是Client向service_manager发起获取服务的请求,service_manager根据Client传过来的服务名在"Binder引用组成的单链表"中找到对应的Server信息并将该Server的binder引用返回给Client进程,Client进程获取到这个Binder引用信息之后根据该Binder引用创建出一个对应该Server的远程服务,这个远程服务就是Server进程的远程代理,Client调用该远程代理的方法就相当于调用Server进程的对应方法。
Binder驱动
binder驱动是整个binder机制的核心。
我们知道两个进程要进行通信必须借助内核空间来做中转,而对于Android而言,在内核中起中转作用的就是binder驱动,binder驱动是处于内核空间的。
Binder驱动在IPC关键节点的作用有:
当某个Server要将自己注册进service_manager进程时,Server进程通过调用ioctl()方法向Binder驱动发起向0号进程(Binder引用为0)注册自己的请求,因为0号进程默认对应的就是service_manager进程所以Binder驱动接收到该请求时会将该请求转发给service_manager进程。
在Server注册这一流程里其实Binder引用具体做的就是2件事:
- 创建该Server对应的Binder实体
- 在service_manager中的"保存Binder引用的红黑树"中查找是否存在指向该Server的Binder实体的Binder引用,如果没有则新建该Binder引用并将其加入service_manager中的"保存Binder引用的红黑树中"。
service_manager则会将该Server的相关信息(服务名+Binder引用)存入其内部的"Binder引用组成的单链表"中。
当某个Client要获取某个Server服务时,Client其实也是通过ioctl()向Binder驱动请求获取某个服务,Binder驱动在收到该请求时因为Client请求ioctl()时指定了target是0也就是要从service_manager进程中获取某个服务,所以Binder驱动会将请求转发给service_manager。service_manager在收到Binder驱动的转发之后会从"Binder引用组成的单链表"中查找对应的Server的相关信息,如果存在则将该Server相关信息给Binder驱动,Binder驱动就会转发给Client端。而Client收到Binder驱动反馈的Server相关信息之后根据里面的Binder引用创建出与Server对应的远程服务。这个远程服务其实是Server进程的远程代理,Client调用该远程代理的方法就相当于直接调用Server对应的方法。
因为Client调用该Server的远程服务接口时,该远程服务会对应的通过Binder驱动和真正的Server进行交互,从而执行相应的动作。
远程服务
如果Client和Server处于同一进程那二者之间的通信就特别简单,Client端只需要获取Server端的对象然后直接调用即可。但是在Binder机制中Client和Server不处于同一进程,这就意味着不能直接通过获取对象来进行通信。所以就需要Server提供一个接入点给Client端,该接入点就是"Server的远程服务代理"!!!
client能获取到Server远程服务那它就相当于Server的代理。Client要和Server通信值需要调用该远程服务代理相应方法即可,其他的工作都交给Binder驱动来完成。