接下来的一段时间,我们会来深入认识一下Android
中的Binder
机制。
今天的主要是来对Binder
做一个较全面的介绍,为之后的深入分析做一个预热准备。
Linux IPC
首先Binder
是Android
中的一种独有的跨进程通信方式,简称IPC
。它是专门为Android
平台设计的。
那为什么要设计出Binder
这个烦人的东西呢?我们都知道Android
是基于Linux
系统进行演变过来的,所以理应也能直接使用Linux
的IPC
通信方式。
所以在理解Binder
的设计初衷之前,我们先来了解一下Linux
系统中现有的IPC
通信方式。
Linux
现有的IPC
通信方式有6
种:
- 管道
- 信号量
- 信号
- 共享内存
- 消息队列
- socket
管道
英文为pipe
,在Linux
中它的本质是一个文件系统,通过一个进程以写的方式打开文件,另一个进程以读的方式进行打开文件,通过这样读写的方式,实现了进程间的通信。
只不过该文件是位于Linux
内存中,所以操作管道就是以文件的方式操作Linux
内存缓存区。
由于管道是通过读写文件的方式进行运作的,所以它需要进行两次数据的拷贝;分别是copy_form_user
从写进程拷贝到文件内存缓存区,再通过copy_to_user
从文件缓存区拷贝到读进程中。
同时管道还有大小限制,默认为4k
,一旦写入端超过大小限制,管道将会阻塞。
信号量
主要作用于进程间的资源互斥访问,通过PV
两种操作等待与发送信号。
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行 V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
所以信号量主要是用来解决多个进程对同一资源的竞争问题,类似于多线程的同步锁。
信号
Linux
中定义的一种软中断,有64
种,分为可靠信号与不可靠信号,多用于消息传递与通知,不适合传递信息。
共享内存
共享内存顾名思义,允许不同的进程访问同一块内存地址空间,它也需要进行两次数据拷贝操作,分别是将数据拷贝到共享内存中,又从共享内存中间将数据拷贝出来。
但需要注意的是,共享内存是不提供同步机制。
意思就是说,在其中一个进程进行写操作时,并不能放在另一进程进行读操作。
为了解决这个问题,共享内存一般都与前面说的信号量一起使用。
消息队列
消息队列通过一个进程向另一个进程发生消息块的方式进行通信,它与管道非常类似,都需要发送与接收,数据拷贝两次。
不同点是
- 消息队列可以防止同步与阻塞问题。
- 消息队列的接收方可以进行选择性接收。
- 发送的消息块有最大限制
socket
Linux
中的socket
是基于C/S
架构的,传输效率低,多用于跨网络与跨设备的通信。
在Android
底层使用socket
来进行init
与zygote
等进程间的通信。
最后简单的来看一张图来了解在Linux
中不同进程中的通信过程。
所以通过上面的分析,Linux
现有的几种IPC
通信方式都不是很适合Android
间的进程通信。
例如管道、共享内存与消息队列都需要拷贝两次数据,同时有的还会存在阻塞与同步问题;另外的信号、信号量与socket
由于使用场景的原因,都不适合用于Android
中快速的进程间的数据通信。
Binder
那么Binder
通信方式是怎么样的呢?
Binder
本身是基于C/S
架构的,层次分明,架构稳定,同时Binder
内部只需使用一次数据拷贝操作,就能达到进程间数据的通信;另外Binder
还支持鉴别用户进程的Uid
,为Android
提供身份的验证。
我们先来通过一张图来简单看下基于Binder
的进程通信过程
Binder
数据通信流程是,将数据从client
端拷贝到内核空间,在内核空间中会提前通过mmap
方式建立与server
端的内存地址映射,通过内存地址映射server
端可以直接访问内核空间中的数据,从而避免将数据拷贝到server
端,提高进程间的通信速度。
在整个通信的过程中主要做的事情是:
- 通过
/dev/binder
打开binder
驱动 - 通过
mmap
建立内存地址映射 - 通过
ioctl
与binder
驱动交互,进行数据传输 client
使用BpBinder
的transact
方法进行事务请求server
使用BBinder
的onTransact
方法来接收相应的事务
由于Android
中主要使用Binder
来进行service
的注册与获取,所以为了更好的管理service
的注册,使用了ServiceManager
来进行统一管理service
的注册。
在service
的注册过程中ServiceManager
就相当于server
端,内部开启loop
循环,不断接收消息将注册的service
保存到注册表svclist
中。
最终关于Binder
的整个大致流程如下图所示
其中client
与server
位于应用层,ServiceManager
位于用户空间,binder
驱动位于内核空间。
对于开发者来说,用户空间与内核空间是透明的,我们只需关注应用层client
与server
的实现,就可以方便的使用Binder
的通信机制。
以上是对Binder
的一个理论分析,接下来的一段时间,我将结合源码来分析service
的注册过程,从而探索Binder
的整个工作流程,感兴趣的读者可以关注一下。