作者:HenAndroid
本篇文章我就来扒一扒Android 进程间的通信-Binder 机制。
我们都知道Android 是基于Linux系统来实现的,因此,我们有必要来了解一下,为什么Android 不使用Linux本身有的进程通信机制,而是要自己撸一个Binder 这玩意来实现进程间通信。 接下来,我们简要的介绍下Linux 进程间通信的几种方式。介绍Linux IPC机制,旨在了解其思想,得出其优缺点,便于找到Android中使用Binder 的依据,故不做深入的分析,如果想要深入分析,可以查看本文末尾提到的参考文献列表,读者自行去查阅。
Linux 下IPC机制
1.管道通信
管道文件的特性
- 打开管道必须有两端(两个进程)同时打开一个管道。分别为读(r)和写(w);读取端负责从管道中读取数据,写入端负责向管道中写入数据。
- 当读端关闭,写端会收到信号,终止程序;当写端关闭,读端不再进入阻塞。
- 管道文件大小始终为0, 打开管道文件时,为其分配内存空间,管道关闭后数据清除;Linux默认PIPE_SIZE=64K(一个管道最大存储空间);PIPE_BUF(管道缓冲区)=4k;当对管道进行write操作时,若此时有多个进程同时写入一个管道,且写入的数据大小超过PIPE_BUF 时,则写入的数据可能互相穿插(数据不安全)。
管道文件的内存空间中有两个指针head和tail. head 指针随读取数据向后移动,tail 指针随写入数据向后移动。当指针到文件尾时,会造成读阻塞(读取结束)/写阻塞(空间已满)
管道的局限性
- 数据只能自己读,不能自己写
- 数据一旦被读走,管道中便不存在,不可重复读取
- 由于管道采用半双工通信方式,因此,数据只能再一个方向上流动
- 只能在有公共祖先的进程中实现通信
小结:管道通信是一种1v1 的通信方式,相对来说比较安全的。缺点是:数据只能单向传输。
2.共享内存
共享内存就是允许两个或多个进程共享一定的存储区。当共享的这块存储区中的数据发生改变,所有共享这块存储区的进程都会察觉数据的改变,因为数据不需要在客户端和服务端进行数据拷贝,数据直接写到内存,不用若干次数据拷贝,所以这是最快的IPC。
共享内存的优势
- 因为两个进程通过地址映射到同一片物理地址,所以,进程可以给那一片物理空间中写入数据,也可以读取数据----进程间双向通信。
- 客户端和服务端都是从内存中直接读取、写入数据的,没有涉及到数据的拷贝,因此,速度最快。
- 生命周期随内核,不会随着服务端或者客户端的断开而销毁。所有访问共享内存对象的进程都结束了,共享内存区域对象依然存在。
共享内存的缺点
共享内存并未实现同步机制:多个进程可以同时写入数据,这就会造成数据的混乱
小结
是一种多对多的IPC,访问速度最快的IPC。但是,没有提供同步机制,不够安全
3.Socket 通信
Linux 的哲学思想是"一切皆文件",所以可以使用 打开(open)–> 读写(read/write)–> 关闭(close) 模式来实现,Socket 就是该模式的一个实现,Socket是一种特殊的文件。
使用套接字除了可以实现网络间不同主机间的通信,也可以实现同一主机下不同进程间的通信。且建立的通信是双向的通信,通信流程如下图。从图中就可以看出,客户端与服务端要建立连接,然后读写数据,相当于在两个进程间各自拷贝数据,然后传输数据,这个效率是很慢的。
小结
Socket 是一种双向多对多模式的IPC机制之一,但数据发生了两次拷贝,因此效率比较低。但是比较安全的。
再看Linux 几种IPC方式的优缺点:
因此,Android 就决定自己实现一套IPC的实现方式----Binder Binder只需要一次拷贝,且足够安全。
Android IPC通信Binder
在介绍Binder 之前,我们先来了解下以下概念:
1.内核空间和用户空间
- 内核空间(内核进程):操作系统所占用的内存区域 -------只有一份
- 用户空间(用户进程):用户进程所在的内存区域 ---------多份
直白的说,我们开发的APP所对应的字节码就存储在用户空间,其所进行的一系列方法调用,内存的分配都是在用户空间中。而Android系统的代码就在内核空间中。
Q:为什么要这么划分?
A:使用内核空间和用户空间这种分开来划分,可以做到,每个APP(用户空间)不会影响其他APP,也不会造成系统的崩溃。
使用adb shell ps 可以看到下图
可以看到手机中PPID为0 的就是内核进程。
2.物理地址和虚拟地址
2.1 虚拟内存
实际上我们写的程序,都是面向虚拟内存的,我们在程序中写的变量的地址,实际上是虚拟内存中的地址,当CPU想要访问该地址的时候,内存管理单元MMU就会将虚拟地址翻译成物理地址。然后,CPU就可以从真实的物理地址处获取到数据。
2.2 MMU:内存管理单元
内存管理单元MMU:它是一个硬件,不是软件。它用于将虚拟地址翻译成实际的物理内存地址,同时它还可以将特定的内存块设置成不同的读写属性,进而实现内存保护。注意,MMU是硬件管理,不是软件实现内存管理。
总结来说,MMU能实现以下功能:
- 虚拟内存。有了虚拟内存,可以在处理器上运行比实际物理内存大的应用程序。为了使用虚拟内存,操作系统通常要设置一个交换区(通常在硬盘上),通过将内存中不活跃的数据与指令放到交换区,以腾出物理内存来为其他程序服务。
- 内存保护。通过这一功能,可以将特定的内存块设置为读、写或者可执行的属性。比如将不可变的数据或者代码设为只读的,这样可以防止被恶意串改。
本文只要知道MMU是将虚拟地址转换成物理地址的过程是MMU的核心。
因为程序具有局部性原则,所以在某一段很小的时间段内,只有很少一部分代码会被CPU执行。 每次从磁盘的缓冲区中加载4K的数据到内存中。
2.2.1 页
我们知道物理内存中执行的是可执行的少部分程序代码和数据,这每次加载可执行的少部分代码和数据这个大小是多少呢?
以存储单元为单位来管理显然不现实,因此Linux把虚存空间分成若干个大小相等的存储分区,Linux把这样的分区叫做页。为了换入、换出的方便,物理内存也就按也得大小分成若干个块。由于物理内存中的块空间是用来容纳虚存页的容器,所以物理内存中的块叫做页框。 页与页框是Linux实现虚拟内存技术的基础。
为了CPU高效且方便管理内存,每次需要拿一个页的代码----连续的存储空间=4K,也叫做块。
由于物理内存和虚拟内存分为页和页框的概念,因此,上图中分为了页框码(页码)、偏移量两部分。 页框码(页码)是识别页框(页)的编码,偏移量是识别存储单元在页框(页)内的地址编码。
2.2.2页表
为了将物理内存中的页框和虚拟内存中的页一一对应,由此就产生了页表的概念。页表这个数据结构存储的就是物理内存中的页框码以及虚拟内存中的页码
通过这个页表就可以从虚拟内存找到物理内存所对应的地址编码,从而获取到物理内存中所对应的数据。就实现了映射关系-----通俗点理解:map这个数据结构有点类似。我们知道这个内存映射,了解其设计思想就可以了。
文章最后,我们来简单看一下Android跨进程通信 Binder中的一直所说的只需一次拷贝----就是用到了内存映射的这个核心。
这是Binder IPC 通信模型的经典图,原本两个进程间数据的交互需要两次拷贝(发送数据方将数据拷贝到内核空间,然后内核空间再将数据拷贝到接收方),由于使用了mmap(内存映射)就减少了一次数据接收进程的数据拷贝(也可以说是服务端)。这样Binder只需要在数据发送进程(客户端)实现一次拷贝数据到内核空间即可。
总结
本文从Linux进程间的几种IPC机制谈起,简要分析了其各种IPC机制的优劣,从而引出Android 设计出Binder这种跨进程通信模型。该文是Binder 通信的Linux 基础篇,旨在帮助读者对跨进程通信的几种机制有个大概的了解,从而更好的理解为什么那么Android要设计Binder。
原文地址:https://blog.csdn.net/weixin_61845324
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。