解密 Android IPC 机制_原理篇

588 阅读4分钟

我正在参加「掘金·启航计划」

Binder 底层原理和优势

前面我们讲到了需要使用到 Binder 来进行进程间通信,但是为什么 Binder 可以进程间通信呢?不知道这个问题大家是否有思考过,下面我们就来看看 Binder 的底层通信原理,从原理上面来看一下其被 Android 所选择的优势是什么。

首先,我们先来看一下在 Linux 系统中的进程通信原理,在 Linux 中将寻址空间分为用户空间以及内核空间。内核空间是系统内核运行的空间,用户空间是用户程序运行的空间,为保证内核的安全性和系统的安全性,内核空间和系统空间是隔离的。

但是由于用户空间必然会需要和内核空间进行交流,所以就有一套在用户空间和内核空间的信息交流方式。两个空间突破隔离的方式就是使用系统调用,这样子可以保证所有访问内核空间的方式都是在内核的控制之下进行的,可以保证访问的安全性。而这个过程中,如果进程陷入了内核代码中执行的时候,那么进程就属于内核态中,如果是在执行用户程序自己的代码的时候,那么就是处于用户态中。

而在 Linux 中,就是利用系统调用,将需要发送的数据放到内核空间开辟的一块内核缓存区中,然后接收数据的进程则将内核缓存区中的数据又放到自己的一块内存缓存区中。这样子就完成了一次进程间通信。需要注意到的是,这一次通信,需要使用到两次的复制。

然后我们来看一下 Android 中的 Binder 的运行原理。Binder 的通信使用的是 Binder 驱动,这个驱动不是 Linux 内核里面的,是一个动态可加载的内核,是在 Android 系统中独有的。而 Binder 驱动使用到的数据传递方法是内存映射,这个内存映射是通过 mmap() 实现的。

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。

一次完整的 Binder IPC 通信过程通常是这样:

  1. 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
  2. 接着在内核空间开辟一块内核缓存区,建立内核缓存区内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区接收进程用户空间地址的映射关系;
  3. 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

所以说,由于内存映射的存在,数据的拷贝次数就是只需要一次就可以了,比共享内存性能稍微差一些,但是 Binder 的 C/S 架构让进程通信机制更加的稳定、使用更加方便;而且 Binder 的安全性也是更好的,其为每个 APP 都分配 UID,让进程鉴定机制更加的完备。

最后提一点需要注意的地方,建立通信服务端(Service)的时候,是需要在 ServiceManager 处注册的,然后注册时候告知需要返回的 Binder 是哪个存入到注册表中,最后客户端(Client)再通过 ServiceManager 获取到注册表中对应的 Binder。但是需要注意的是,Service 与 ServiceManager 的通信也是使用 Binder 来通信的,而这里面的 Binder 是系统自动创建好的。

参考

序列化和反序列化有什么作用?java序列化和反序列化的作用小官学长的博客-CSDN博客

关于Java中Serializable的一些问题_viclee108的博客-CSDN博客

教妹学Java(九):一文搞懂Java中的基本数据类型沉默王二的博客-CSDN博客java基本数据类型

Intent、Bundle传递数据的那些秘密_请叫我鲜鲜哥的博客-CSDN博客

写给 Android 应用工程师的 Binder 原理剖析 - 掘金 (juejin.cn)

Java中线程安全的集合java线程安全的集合Willing卡卡的博客-CSDN博客