前言
我们在上一篇中比较详尽的介绍了Android的消息机制,不过有一些内容我们在不理解Android Binder的话是无法讲解清楚的。对于初学Android的朋友而言,最难却又最想掌握的恐怕就是Binder机制了,因为Android系统基本上可以看作是一个基于Binder通信的C/S架构。 Binder就像网络一样,把系统的各个部分连接在了一起,因此它是非常重要的。我们下面会Android Binder机制进行从上到下从易到难的分层次讲解,从而既能让初学者对Binder有一定认识,也能让有一定Android基础的人获得收获。
注:下文中的源码均出自android-6.0.0_r5
Binder概述
对于初学者来说,深入Android Binder机制是非常不明智的。Android Binder机制大都涉及Java层、Native层、驱动层这三三个方面,对于初学者来说想啃这三块硬骨头很容易磕着牙。我们这这一节概述从以下几个方面让你从比较宏观的角度理解Android Binder。
进程
在该系列博客中的第一章我们就说起了Android进程相关问题,Android故意弱化了进程的概念,而用相对容易理解的四大组件。可是我们在稍微深入Android的时候,那么进程是绕不过的。默认情况下,同一个应用程序中的所有组件运行在同一个进程中,而且绝大多数的应用程序也都是这样的。这个默认进程是用这个应用的包名来命名的。
进程间通信
我们在运行App的时候经常需要使用一些系统服务,比如剪切板服务,而剪切板服务是运行在SystemServer进程中的。那我们的App是怎么使用剪切板服务的呢,我们都知道进程是相互独立的,静态变量等等都无法共用。这就涉及到进程间的通信了,即IPC。我们都知道Android是基于Linux内核的,那我们简单介绍下Linux下的几种IPC机制。
管道(Pipe)
管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
- 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。当父进程与使用fork创建的子进程直接通信时,发送数据的进程关闭读端,接受数据的进程关闭写端。
- 管道只能在本地计算机中使用,而不可用于网络间的通信。
命名管道(FIFO)
命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了管道的弊端,他可以允许没有亲缘关系的进程间通信。
共享内存(Share Memory)
共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到自己的地址空间中。所有进程都可以访问共享内存中的地址,如果一个进程向共享内存中写入了数据,所做的改动将立刻被其他进程看到。
- 共享内存是IPC最快捷的方式,共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是同一块的物理空间,仅仅映射到各进程的地址不同而已,因此不需要进行复制,可以直接使用此段空间。
-
共享内存本身并没有同步机制,需要程序员自己控制。
内存映射(Memory Map)
内存映射是由一个文件到一块内存的映射,在此之后进程操作文件,就像操作进程空间里的内存地址一样了。
套接字(Socket)
套接字机制不但可以单机的不同进程通信,而且使得跨网机器间进程可以通信。
套接字的创建和使用与管道是有区别的,套接字明确地将客户端与服务器区分开来,可以实现多个客户端连到同一服务器。
Binder
作为Android系统下的一种IPC机制,其本质上与上面罗列出的IPC机制并无本质上的不同,都是作为进程间通信的一种手段。并且在Android系统中也不是只存在Binder这一种进程间通信的方式,在有些地方也使用了Socket。既然Linux已经提供了众多IPC机制,那么Android 为何还要使用Binder作为主要的进程间通信的方式呢,那么当然有他的优点存在。
- 采用C/S的通信模式。而在linux通信机制中,目前只有socket支持C/S的通信模式,但socket有其劣势,具体参看第二条。
- 有更好的传输性能。对比于Linux的通信机制,socket:是一个通用接口,导致其传输效率低,开销大;管道和消息队列:因为采用存储转发方式,所以至少需要拷贝2次数据,效率低;共享内存:虽然在传输时没有拷贝数据,但其控制机制复杂(比如跨进程通信时,需获取对方进程的pid,得多种机制协同操作)。
- 安全性更高。Linux的IPC机制在本身的实现中,并没有安全措施,得依赖上层协议来进行安全控制。而Binder机制的UID/PID是由Binder机制本身在内核空间添加身份标识,安全性高;并且Binder可以建立私有通道,这是linux的通信机制所无法实现的(Linux访问的接入点是开放的)。
- 对用户来说,通过binder屏蔽了client的调用server的隔阂,client端函数的名字、参数和返回值和server的方法一模一样,对用户来说犹如就在本地(也可以做得不一样),这样的体验或许其他ipc方式也可以实现,但binder出生那天就是为此而生。
Java层Binder
Java层Binder的功能,依赖于Native层Binder来实现,可以认为Java层Binder架构是Native层Binder架构的一个镜像。但是这并不影响我们分析Android Java层Binder的功能。我们用一个例子来说明这个过程。
我们在第一篇中就讲解了SystemServer这个进程,这个进程和zygote进程一起撑起了Android 世界,他们之中有一个崩溃,Android世界就会砰然倒塌。Android许多的重要的系统服务如AMS、PMS等都运行在SystemServer进程中。但是还有一个比较重要的进程ServiceManager进程(简称SM)跟zygote是兄弟进程。这个进程的作用是用来统一管理服务,如AMS。它们之间的关系如下。
我们的AMS需要向SM进程中注册信息,其他进程如果想使用AMS,那么先和ServiceManager进程进行通信查询,接着再和AMS所在SystemServer进程通信。这部分关系图如下