Potato icon indicating copy to clipboard operation
Potato copied to clipboard

IPC And Binder

Open yunshuipiao opened this issue 5 years ago • 0 comments

IPC And Binder

[TOC]

Binder 作为 Android 中进程间通信(IPC)的一种方式, 这篇文章简要的叙述 Binder 机制。

Linux 的 IPC

由于 Android 系统是基于 Linux,这里有一个问题,为什么不采用 Linux 的 IPC 方法。

那么首先对 Linux 的 IPC 方式有个大概了解

  1. 管道:在创建时分配一个 page 大小的内存,缓存区域大小有限
  2. 消息队列:信息复制两次,额外的 CPU 消耗,不适合评判或者信息量大的通信
  3. 共享内存:无需复制,共享缓冲区直接附加到进程虚拟地址空间,速度快;但进程间同步问题操作系统无法实现,必须利用同步工具解决。
  4. 套接字:作为更通用的接口,传输效率低,主要用于不通机器网络或者跨网络的通信
  5. 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问。
  6. 信号:不适用于信息交换,更适用于进程中断控制。

下面对 binder 和这几种方式做个简单对比:

  1. 从性能的角度:数据拷贝的次数,BInder 只需要拷贝一次,而管道,消息队列,Socket 都需要2次,共享内存不许拷贝;所以进次于共享内存

  2. 从稳定的角度考虑:Binder 基于C/S 架构,也即服务端/客户端的架构。Server 和 Client 相对独立,稳定型比较好;而共享内存实现复杂,需要考虑访问临界资源的并发同步问题;所以 Binder 在这方面要优于共享内存

  3. 从安全的角度:传统 Linux IPC 的接收方法无法获得对方进程可靠的 UID/PID, 从而无法鉴别对方身份;而 Android 作为一个开放的开源体系,来源广泛,因为安全性非常重要;对于普通用户,不希望从 App 商店下载隐私数据,后台造成手机耗电等问题。Android 为每个 App 都分配了单独的 UID ,可以用来鉴别身份。另外 Android 系统只对外暴露 Client 端,Client 端将任务发送给 Server 端,Server 会根据权限控制策略,让用户选择时候继续。

    因此在上面的几个角度对比之下,Android 的研究人员开发 Binder 作为进程间通信的方法。

Android 中的多进程

可以通过给 四大组件指定 android:process 属性开启多进程。

多进程带来的问题:通过共享数据带来

  • 静态成员和单例模式失效
  • 线程同步机制失效
  • SharedPreference 的可靠性下降
  • Application 多次创建

IPC 基础概念介绍

Serialozation 接口

java 的序列化接口,为对象提供标准的序列化和反序列化操作。

Parcelable 接口

Android 特有的序列化接口,实现稍微复杂。

实现这个接口的类,其对象就可以序列化,通过 Intent 或者 Binder 传递。

##Binder 是什么

上面简单提到了 Binder 的几个特点, 作为 Android 的 IPC 的方式,基于 C/S 架构,通信时数据拷贝一次等等。

对于 Binder 的定义,在不同的场景下有不同的定义:

  • 从机制,模型角度:Binder 是一种 Android 中进行 IPC 的方式,其 Binder 机制模型,用于在 Android 中实现 IPC。
  • 从模型结构,组成:Binder 是一种虚拟的物理设备驱动,用于连接 Server 进程,Client 进程和 Service Manager 进程。
  • 从 Android 代码的实现角度:Binder 是一个类,实现了 IBinder 接口。具体实现在 Android 中。

Linux 进程空间

首先是几个基础知识:

  1. 一个进程空间划分为 用户空间 和 内核空间(Kernel), 即把进程内 用户 和 内核隔离开

    区别在于:

    • 进程间 用户空间的数据不共享,内核空间的数据共享
    • 所有进程公用一个内核空间
  2. 进程间 用户空间和内核空间 进行交互 需通过 系统调用,主要通过函数:

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

进程隔离 和 IPC

为了保证安全性和独立性,一个进程不能直接操作或者访问另一个进程概念,Android 中进程间是相互独立和隔离的。

这时两个进程需要通信,A 进程将数据拷贝到内核空间,接着再拷贝到 B进程 用户空间,实现交互。其缺点也很明显,数据拷贝两次,并且缓存也需要对方提供,不知大小。

而 Binder 是:连接两个进程,实现 mmap() 系统调用,创建数据接收的缓存空间 和管理数据接收缓存。拷贝一次数据,使用内存映射

Binder IPC 模型

Binder 基于C/S 架构,有四个部分组成:Client 进程,Server 进程,ServiceManger 进程和 Binder 驱动。

Binder驱动:

一种虚拟物理设备,连接 Server,Client 和 ServiceManger 进程。使用内存映射,即 mmap() 函数,实际上创建数据接收的缓存空间。

核心原理就是 内存映射,其工作流程如下:

注册服务

  1. Server 进程向 Binder 驱动发起服务注册请求
  2. Binder 驱动将注册请求转发给 ServerManger 进程
  3. ServerManger 进程添加该 Server 进程(已注册服务)

获取服务

  1. Client 向Binder 驱动发起获取服务的请求,传递要获取的服务名称
  2. Binder 驱动将该请求转发给 ServiceManger 进程
  3. ServiceManger 查找到 Client 需要的 Server 对应的服务信息
  4. 通过 Binder 驱动将上述服务信息返回给 Client 进程

使用服务

  1. Binder 驱动为进程间通信做准备:实现内存映射。Binder 驱动 创建一个接收缓存区,根据 Server 信息找打对应的 Server 进程,实现内核缓存区和 Server 进程用户空间地址 同时映射到 同一个接收缓存区中。此时还未传输数据
  2. Client 进程将参数数据发送到 Server 进程。Client 进程通过系统调用 copy_from_user() 发送数据到内核空间的缓存区(当前线程被挂起),Binder 驱动通知 Server 进程解包。
  3. Server 进程根据 Client 进程要求 调用目标方法。收到 Binder 驱动通知后,Server 进程从线程池中取出线程,进行数据解包,调用目标方法;将最终执行结果写入到自己的共享内存中。
  4. Server 进程将目标方法的结果返回给 Client 进程。将最终执行结果写入存在映射 的用户空间的内存区域中;Binder 驱动通知 Client 进程获得返回结果(重新唤醒线程),Client 进程通过系统调用 copy_to_user() 从内核缓存区接收 Server 进程返回的结果。

deadObject异常处理

由于客户端是通过bindService绑定服务端的service,而Android系统运行环境复杂,服务随时都可能被kill,如果此时再次调用服务端的接口,会引起deadObject异常的发生。

解决办法:使用之前先判断bind是否还存活

Android 中的 IPC 方式

名称 优点 缺点 适用场景
Bundle 简单易用 只能传输 Bundle 支持的数据类型 四大组件间的 IPC
文件共享 简单易用 不适合高并发场景,并且无法做到进程间即时通信 无并发访情况,交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发通信,实时通信 使用复杂,需要处理好线程同步 一对多且有 RPC 需求
Messenger 功能一般,支持一对多串行通信,实时通信 不能很好的处理并发情形,数据通过 Message 进行传输,只支持 Bundle。 低并发的一对多即时通信
ContentProvider 在数据源访问方面功能强大,支持一对多并发数据共享,扩展操作 受约束的 AIDL,提供数据的 CURD 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现复杂 网络数据交换

总结

上述简单的对 Android 的 Binder 机制做了简单了解,涉及为什么要使用 Binder,以及和其他方式优缺点的对比。Binder 机制的模型和通信原理和过程,以备面试

yunshuipiao avatar May 18 '19 10:05 yunshuipiao