写点什么

Android 高阶:了解这些知识点,学习 Binder 就不成问题 (1)

用户头像
Android架构
关注
发布于: 刚刚

共享内存 多个进程可以直接读写的一块内存空间,是针对其他通信机制运行效率较低而设计的。 为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大的提高效率。


套接字 套接字是更为基础的进程间通信机制,与其他方式不同的是,套接字可用于不同机器之间的进程间通信。

1.2 Android 中的 IPC 机制

Android 系统是基于 Linux 内核的,在 Linux 内核基础上,又拓展出了一些 IPC 机制。Android 系统除了支持套接字,还支持序列化、Messenger、AIDL、Bundle、文件共享、ContentProvider、Binder 等。Binder 会在后面介绍,先来了解前面的 IPC 机制。


序列化 序列化指的是 Serializable/P


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


arcelable,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,例如通讯录,音视频等,这些操作本身就是跨进程进行通信。

2.Linux 和 Binder 的 IPC 通信原理

在讲到 Linux 的进程通信原理之前,我们需要先了解 Liunx 中的几个概念。



内核空间和用户空间 当我们接触到 Liunx 时,免不了听到两个词,User space(用户空间)和 Kernel space(内核空间),那么它们的含义是什么呢? 为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间。Linux 操作系统将最高的 1GB 字节供内核使用,称为内核空间,较低的 3GB 字节供各进程使用,称为用户空间。


内核空间是 Linux 内核的运行空间,用户空间是用户程序的运行空间。为了安全,它们是隔离的,即使用户的程序崩溃了,内核也不会受到影响。内核空间的数据是可以进程间共享的,而用户空间则不可以。比如在上图进程 A 的用户空间是不能和进程 B 的用户空间共享的。


进程隔离 进程隔离指的是,一个进程不能直接操作或者访问另一个进程。也就是进程 A 不可以直接访问进程 B 的数据。


系统调用 用户空间需要访问内核空间,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。


进程 A 和进程 B 的用户空间可以通过如下系统函数和内核空间进行交互。


  • copy_from_user:将用户空间的数据拷贝到内核空间。

  • copy_to_user:将内核空间的数据拷贝到用户空间。


内存映射 由于应用程序不能直接操作设备硬件地址,所以操作系统提供了一种机制:内存映射,把设备地址映射到进程虚拟内存区。 举个例子,如果用户空间需要读取磁盘的文件,如果不采用内存映射,那么就需要在内核空间建立一个页缓存,页缓存去拷贝磁盘上的文件,然后用户空间拷贝页缓存的文件,这就需要两次拷贝。 采用内存映射,如下图所示。



由于新建了虚拟内存区域,那么磁盘文件和虚拟内存区域就可以直接映射,少了一次拷贝。


内存映射全名为 Memory Map,在 Linux 中通过系统调用函数 mmap 来实现内存映射。将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间,反之亦然。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。

2.1 Linux 的 IPC 通信原理

了解 Liunx 中的几个概念后,就可以学习 Linux 的 IPC 通信原理了,如下图所示。



内核程序在内核空间分配内存并开辟一块内核缓存区,发送进程通过 copy_from_user 函数将数据拷贝到到内核空间的缓冲区中。同样的,接收进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程。这样数据发送进程和数据接收进程完成了一次数据传输,也就是一次进程间通信。


Linux 的 IPC 通信原理有两个问题:


  1. 一次数据传递需要经历:用户空间 --> 内核缓存区 --> 用户空间,需要 2 次数据拷贝,这样效率不高。

  2. 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,浪费了空间或者时间。

2.2 Binder 的通信原理

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 次拷贝,不会因为不知道数据的大小而浪费空间或者时间,效率更高。

3.为什么要使用 Binder

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 采用的是信号。

4.为什么要学习 Binder?

Binder 机制在 Android 中的地位举足轻重,我们需要掌握的很多原理都和 Binder 有关:


  1. 系统中的各个进程是如何通信的?

  2. Android 系统启动过程

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android高阶:了解这些知识点,学习Binder就不成问题(1)