原文:刘望舒
Binder原理是掌握系统底层原理的基石,也是进阶高级工程师的必备知识点,这篇文章不会过多介绍Binder原理,而是讲解学习Binder前须要的掌握的知识点。android
我认为学好Binder原理的秘诀主要有两点:面试
本篇文章的目的就是帮助你们完成第1点,第二点会在后续的文章进行详细介绍。缓存
IPC全名为inter-Process Communication,含义为进程间通讯,是指两个进程之间进行数据交换的过程。在Android和Linux中都有各自的IPC机制,这里分别来介绍下。安全
Linux中提供了不少进程间通讯机制,主要有管道(pipe)、信号(sinal)、信号量(semophore)、消息队列(Message)、共享内存(Share Memory)、套接字(Socket)等。架构
管道
管道是Linux由Unix那里继承过来的进程间的通讯机制,它是Unix早期的一个重要通讯机制。管道的主要思想是,在内存中建立一个共享文件,从而使通讯双方利用这个共享文件来传递信息。这个共享文件比较特殊,它不属于文件系统而且只存在于内存中。另外还有一点,管道采用的是半双工通讯方式的,数据只能在一个方向上流动。
简单的模型以下所示。并发
信号
信号是软件层次上对中断机制的一种模拟,是一种异步通讯方式,进程没必要经过任何操做来等待信号的到达。信号能够在用户空间进程和内核之间直接交互,内核能够利用信号来通知用户空间的进程发生了哪些系统事件。信号不适用于信息交换,比较适用于进程中断控制。
信号量
信号量是一个计数器,用来控制多个进程对共享资源的访问。它常做为一种锁机制,防止某进程正在访问共享资源时,其余进程也访问该资源。主要做为进程间以及同一进程内不一样线程之间的同步手段。
消息队列
消息队列是消息的链表,具备特定的格式,存放在内存中并由消息队列标识符标识,而且容许一个或多个进程向它写入与读取消息。信息会复制两次,所以对于频繁或者信息量大的通讯不宜使用消息队列。异步
共享内存
多个进程能够直接读写的一块内存空间,是针对其余通讯机制运行效率较低而设计的。
为了在多个进程间交换信息,内核专门留出了一块内存区,能够由须要访问的进程将其映射到本身的私有地址空间。进程就能够直接读写这一块内存而不须要进行数据的拷贝,从而大大的提升效率。ide
套接字
套接字是更为基础的进程间通讯机制,与其余方式不一样的是,套接字可用于不一样机器之间的进程间通讯。函数
Android系统是基于Linux内核的,在Linux内核基础上,又拓展出了一些IPC机制。Android系统除了支持套接字,还支持序列化、Messenger、AIDL、Bundle、文件共享、ContentProvider、Binder等。Binder会在后面介绍,先来了解前面的IPC机制。
序列化
序列化指的是Serializable/Parcelable,Serializable是Java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操做。Parcelable接口是Android中的序列化方式,更适合在Android平台上使用,用起来比较麻烦,效率很高。
Messenger
Messenger在Android应用开发中的使用频率不高,能够在不一样进程中传递Message对象,在Message中加入咱们想要传的数据就能够在进程间的进行数据传递了。Messenger是一种轻量级的IPC方案并对AIDL进行了封装。性能
AIDL
AIDL全名为Android interface definition Language,即Android接口定义语言。Messenger是以串行的方式来处理客户端发来的信息,若是有大量的消息发到服务端,服务端仍然一个一个的处理再响应客户端显然是不合适的。另外还有一点,Messenger用来进程间进行数据传递可是却不能知足跨进程的方法调用,这个时候就须要使用AIDL了。
Bundle
Bundle实现了Parcelable接口,因此它能够方便的在不一样的进程间传输。Acitivity、Service、Receiver都是在Intent中经过Bundle来进行数据传递。
文件共享
两个进程经过读写同一个文件来进行数据共享,共享的文件能够是文本、XML、JOSN。文件共享适用于对数据同步要求不高的进程间通讯。
ContentProvider
ContentProvider为存储和获取数据了提供统一的接口,它能够在不一样的应用程序之间共享数据,自己就是适合进程间通讯的。ContentProvider底层实现也是Binder,可是使用起来比AIDL要容易许多。系统中不少操做都采用了ContentProvider,例如通信录,音视频等,这些操做自己就是跨进程进行通讯。
在讲到Linux的进程通讯原理以前,咱们须要先了解Liunx中的几个概念。
内核空间和用户空间
当咱们接触到Liunx时,免不了听到两个词,User space(用户空间)和 Kernel space(内核空间),那么它们的含义是什么呢?
为了保护用户进程不能直接操做内核,保证内核的安全,操做系统从逻辑上将虚拟空间划分为用户空间和内核空间。Linux 操做系统将最高的1GB字节供内核使用,称为内核空间,较低的3GB 字节供各进程使用,称为用户空间。
内核空间是Linux内核的运行空间,用户空间是用户程序的运行空间。为了安全,它们是隔离的,即便用户的程序崩溃了,内核也不会受到影响。内核空间的数据是能够进程间共享的,而用户空间则不能够。好比在上图进程A的用户空间是不能和进程B的用户空间共享的。
进程隔离
进程隔离指的是,一个进程不能直接操做或者访问另外一个进程。也就是进程A不能够直接访问进程B的数据。
系统调用
用户空间须要访问内核空间,就须要借助系统调用来实现。系统调用是用户空间访问内核空间的惟一方式,保证了全部的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提高了系统安全性和稳定性。
进程A和进程B的用户空间能够经过以下系统函数和内核空间进行交互。
内存映射
因为应用程序不能直接操做设备硬件地址,因此操做系统提供了一种机制:内存映射,把设备地址映射到进程虚拟内存区。
举个例子,若是用户空间须要读取磁盘的文件,若是不采用内存映射,那么就须要在内核空间创建一个页缓存,页缓存去拷贝磁盘上的文件,而后用户空间拷贝页缓存的文件,这就须要两次拷贝。
采用内存映射,以下图所示。
因为新建了虚拟内存区域,那么磁盘文件和虚拟内存区域就能够直接映射,少了一次拷贝。
内存映射全名为Memory Map,在Linux中经过系统调用函数mmap来实现内存映射。将用户空间的一块内存区域映射到内核空间。映射关系创建后,用户对这块内存区域的修改能够直接反应到内核空间,反之亦然。内存映射能减小数据拷贝次数,实现用户空间和内核空间的高效互动。
了解Liunx中的几个概念后,就能够学习Linux的IPC通讯原理了,以下图所示。
内核程序在内核空间分配内存并开辟一块内核缓存区,发送进程经过copy_from_user函数将数据拷贝到到内核空间的缓冲区中。一样的,接收进程在接收数据时在本身的用户空间开辟一块内存缓存区,而后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程。这样数据发送进程和数据接收进程完成了一次数据传输,也就是一次进程间通讯。
Linux的IPC通讯原理有两个问题:
Binder是基于开源的OpenBinder实现的,OpenBinder最先并非由Google公司开发的,而是Be Inc公司开发的,接着由Palm, Inc.公司负责开发。后来OpenBinder的做者Dianne Hackborn加入了Google公司,并负责Android平台的开发工做,顺便把这项技术也带进了Android。
Binder是基于内存映射来实现的,在前面咱们知道内存映射一般是用在有物理介质的文件系统上的,Binder没有物理介质,它使用内存映射是为了跨进程传递数据。
Binder通讯的步骤以下所示。
1.Binder驱动在内核空间建立一个数据接收缓存区。
2.在内核空间开辟一块内核缓存区,创建内核缓存区和数据接收缓存区之间的映射关系,以及数据接收缓存区和接收进程用户空间地址的映射关系。
3.发送方进程经过copy_from_user()函数将数据拷贝 到内核中的内核缓存区,因为内核缓存区和接收进程的用户空间存在内存映射,所以也就至关于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通讯。
整个过程只使用了1次拷贝,不会由于不知道数据的大小而浪费空间或者时间,效率更高。
Android是基于Linux内核的 ,Linux提供了不少IPC机制,而Android却本身设计了Binder来进行通讯,主要是由于如下几点。
性能方面
性能方面主要影响的因素是拷贝次数,管道、消息队列、Socket的拷贝次书都是两次,性能不是很好,共享内存不须要拷贝,性能最好,Binder的拷贝次书为1次,性能仅次于内存拷贝。
稳定性方面
Binder是基于C/S架构的,这个架构一般采用两层结构,在技术上已经很成熟了,稳定性是没有问题的。共享内存没有分层,难以控制,并发同步访问临界资源时,可能还会产生死锁。从稳定性的角度讲,Binder是优于共享内存的。
安全方面
Android是一个开源的系统,而且拥有开放性的平台,市场上应用来源很广,所以安全性对于Android 平台而言极其重要。
传统的IPC接收方没法得到对方可靠的进程用户ID/进程ID(UID/PID),没法鉴别对方身份。Android 为每一个安装好的APP分配了本身的UID,经过进程的UID来鉴别进程身份。另外,Android系统中的Server端会判断UID/PID是否知足访问权限,而对外只暴露Client端,增强了系统的安全性。
语言方面
Linux是基于C语言,C语言是面向过程的,Android应用层和Java Framework是基于Java语言,Java语言是面向对象的。Binder自己符合面向对象的思想,所以做为Android的通讯机制更合适不过。
从这四方面来看,Linux提供的大部分IPC机制根本没法和Binder相比较,而共享内存只在性能方面优于Binder,其余方面都劣于Binder,这些就是为何Android要使用Binder来进行进程间通讯,固然系统中并非全部的进程通讯都是采用了Binder,而是根据场景选择最合适的,好比Zygote进程与AMS通讯使用的是Socket,Kill Process采用的是信号。
Binder机制在Android中的地位举足轻重,咱们须要掌握的不少原理都和Binder有关:
上面只是列了一小部分,简单来讲说,好比系统在启动时,SystemServer进程启动后会建立Binder线程池,目的是经过Binder,使得在SystemServer进程中的服务能够和其余进程进行通讯了。再好比咱们常说的AMS、PMS都是基于Binder来实现的,拿PMS来讲,PMS运行在SystemServer进程,若是它想要和DefaultContainerService通讯(是用于检查和复制可移动文件的系统服务),就须要经过Binder,由于DefaultContainerService运行在com.android.defcontainer进程。
还有一个比较常见的C/S架构间通讯的问题,Client端的MediaPlayer和Server端的MeidaPlayerService不是运行在一个进程中的,一样须要Binder来实现通讯。
能够说Binder机制是掌握系统底层原理的基石。根据Android系统的分层,Binder机制主要分为如下几个部分。
上图并无给出Binder机制的具体的细节,而是先给出了一个概念,根据系统的Android系统的分层,我将Binder机制分为了Java Binder、Native Binder、Kernel Binder,实际上Binder的内容很是多,彻底能够写一原本介绍,可是对于应用开发来讲,并不须要掌握那么多的知识点,所以本系列主要会讲解Java Binder和Native Binder。
Android学习PDF+架构视频+面试文档+源码笔记