Linux和Android的IPC通信简介
1.Linux和Android的IPC机制种类
IPC全名为inter-Process Communication,含义为进程间通信,是指两个进程之间进行数据交换的过程。在Android和Linux中都有各自的IPC机制,这里分别来介绍下。
1.1 Linux中的IPC机制种类
Linux中提供了很多进程间通信机制,主要有管道(pipe)、信号(sinal)、信号量(semophore)、消息队列(Message)、共享内存(Share Memory)、套接字(Socket)等。
1.2 Android中的IPC机制
Android系统是基于Linux内核的,在Linux内核基础上,又拓展出了一些IPC机制。Android系统除了支持套接字,还支持序列化、Messenger、AIDL、Bundle、文件共享、ContentProvider、Binder等。
2.Linux和Binder的IPC通信原理
在讲到Linux的进程通信原理之前,我们需要先了解Liunx中的几个概念。
概念
- 内核空间和用户空间
User space(用户空间)和 Kernel space(内核空间)。内核空间是Linux内核的运行空间,用户空间是用户程序的运行空间。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间。Linux 操作系统将最高的1GB字节供内核使用,称为内核空间,较低的3GB 字节供各进程使用,称为用户空间。 - 系统调用
用户空间需要访问内核空间,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。
进程A和进程B的用户空间可以通过如下系统函数和内核空间进行交互。
copy_from_user:将用户空间的数据拷贝到内核空间。
copy_to_user:将内核空间的数据拷贝到用户空间。 - 内存映射
由于应用程序不能直接操作设备硬件地址,所以操作系统提供了一种机制:内存映射,把设备地址映射到进程虚拟内存区。
举个例子,如果用户空间需要读取磁盘的文件,如果不采用内存映射,那么就需要在内核空间建立一个页缓存,页缓存去拷贝磁盘上的文件,然后用户空间拷贝页缓存的文件,这就需要两次拷贝。由于新建了虚拟内存区域,那么磁盘文件和虚拟内存区域就可以直接映射,少了一次拷贝。
内存映射全名为Memory Map,在Linux中通过系统调用函数mmap来实现内存映射。将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间,反之亦然。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。
2.1 Linux的IPC通信原理
Linux的IPC通信原理内核程序在内核空间分配内存并开辟一块内核缓存区,发送进程通过copy_from_user函数将数据拷贝到到内核空间的缓冲区中。同样的,接收进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程。这样数据发送进程和数据接收进程完成了一次数据传输,也就是一次进程间通信。
Linux的IPC通信原理有两个问题:
- 一次数据传递需要经历:用户空间 --> 内核缓存区 --> 用户空间,需要2次数据拷贝,这样效率不高。
- 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用API接收消息头来获取消息体的大小,浪费了空间或者时间。
2.2 Binder的通信原理
Binder是基于内存映射来实现的,大致原理如下图:
Binder的通信原理
Binder通信的步骤如下
- Binder驱动在内核空间创建一个数据接收缓存区。
- 在内核空间开辟一块内核缓存区,建立内核缓存区和数据接收缓存区之间的映射关系,以及数据接收缓存区和接收进程用户空间地址的映射关系。
- 发送方进程通过copy_from_user()函数将数据拷贝 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
整个过程只使用了1次拷贝,不会因为不知道数据的大小而浪费空间或者时间,效率更高。
(PS:为啥需要两次映射?感觉一次就可以了)
3.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采用的是信号。