在Android中跨进程通信被频繁使用,比如当用户点击按钮跳转到一个新的Activity页面,这时我们的app进程会和SystemServer进程频繁通信来创建一个新的Activity,这种例子不胜枚举。所以作为一个Android应用的开发者,理应理解其原理。在讨论Binder前,我们先列举下Linux支持的进程通信方式都有哪些。

Android进程间通信场景

Linux进程通信方式

为什么要自己实现ipc

通过对比,分别介绍优点

使用?

上层拓展?

Linux进程通信方式

  • 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  • 命名管道 (named pipe) : 命名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  • 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  • 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  • 信号 ( signal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
  • 共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  • 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
  • 文件(File):文件系统对文件描述符的读/写控制,进程间一方对文件写,一方对文件读,达到文件之间的通信,可以是不相关进程间的通信。

Android为什么要自己实现ipc

  • 性能:在移动设备上,由于性能受到各方面的限制,例如电量,所以在使用进程间通信时,需要对性能这块有很大的要求。Socket、消息队列这些,在进程间通信时,数据拷贝需要两次,而内存共享只需拷贝无需拷贝,但实现起来比较复杂。Binder在数据上拷贝只需一次,它通过内存映射的机制实现了一次数据拷贝。
  • 安全:传统的IPC,UID/PID只能由用户在数据包填入,这样会导致接收方无法获取到可靠UID/PID,而且还会存在被恶意程序利用的可能。Binder机制中可靠的身份标志只能在IPC机制的内核中添加的,它从协议本身就支持对通信双方进行身份校验,大大提高了安全性。
  • 稳定:Binder基于C/S架构,使用者与提供服务者相互独立,整体清晰,从而提高了整体的稳定性。

下表是Binder和主要Linux进程通信方式的对比:

IPC方式 效率 单双向 模型 安全性
Binder 双向 多堆多
Pipe 一般 单向 一对一
共享内存 最高 双向 多堆多
Socket 很低 双向 多堆多
File 双向 多堆多

Binder

Binder 是基于内存映射 mmap 设计实现的,我们需要先了解一下 mmap 的概念。

mmap

mmap 是一个 Unix 系统调用,用于将文件或设备映射到进程的地址空间,从而允许进程通过内存访问文件内容或设备数据。mmap 的全称是 “memory map”,它将文件或设备的一部分直接映射到进程的地址空间中,使得对该部分数据的读写操作可以像访问内存一样简单高效。

通过 mmap,进程可以将文件映射到内存中的一个地址区域,然后直接读写该区域的数据,而不需要通过传统的文件 I/O 接口,例如 readwrite。这样可以提高 I/O 操作的性能,特别是对于大文件的处理。此外,mmap 还可以用于创建共享内存区域,使得不同进程之间可以共享数据。

在使用 mmap 后,可以通过修改内存区域的内容来修改文件内容,这种修改会直接反映到原始文件中。因此,mmap 也可以用于实现文件的原子写入或原子更新操作。

总之,mmap 提供了一种灵活高效的方式来访问文件内容或设备数据,并且可以简化对文件和内存的管理和操作。

原理

一次 Binder IPC 通信的过程分为以下几个步骤:

  1. 首先,Binder 驱动在内核空间中开辟出一个数据接收缓冲区

  2. 接着,在内核空间中开辟出一个内核缓冲区

  3. 将内核缓冲区与数据接收缓冲区建立映射关系

  4. 将数据接收缓冲区与接收进程的用户空间地址建立映射关系

  5. 发送方进程通过 copy_from_user 将数据从用户空间复制到内核缓冲区

  6. 由于内核缓冲区与数据接收缓冲区有映射关系,同时数据接收缓冲区与接收进程的用户空间地址有映射关系,所以在接收进程中可以直接获取到这段数据

这样便完成了一次 Binder IPC 通信,它的原理如下图所示:

alt text

通过 mmap,Binder 通信时,只需要经历一次数据复制,性能要优于管道、消息队列、socket等方式,在安全性,易用性方面又优于共享内存。鉴于上述原因,Android 选择了这种折中的 IPC 方式,来满足系统对稳定性、传输性能和安全性方面的要求。

Binder架构

Binder 是一种 C/S 架构,分为 BpBinder(客户端)和 BBinder(服务端),他们都派生自 IBinder。其中 BpBinder 中的p表示 proxy,即代理。BpBinder 通过 transact 来发送事务请求,BBinder 通过 onTransact 来接收相应的事务。

alt text

Binder 采用分层架构设计:

alt text

Binder 一次通信的时序图如下:

alt text