Potato
Potato copied to clipboard
IPC And Binder
IPC And Binder
[TOC]
Binder 作为 Android 中进程间通信(IPC)的一种方式, 这篇文章简要的叙述 Binder 机制。
Linux 的 IPC
由于 Android 系统是基于 Linux,这里有一个问题,为什么不采用 Linux 的 IPC 方法。
那么首先对 Linux 的 IPC 方式有个大概了解
- 管道:在创建时分配一个 page 大小的内存,缓存区域大小有限
- 消息队列:信息复制两次,额外的 CPU 消耗,不适合评判或者信息量大的通信
- 共享内存:无需复制,共享缓冲区直接附加到进程虚拟地址空间,速度快;但进程间同步问题操作系统无法实现,必须利用同步工具解决。
- 套接字:作为更通用的接口,传输效率低,主要用于不通机器网络或者跨网络的通信
- 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问。
- 信号:不适用于信息交换,更适用于进程中断控制。
下面对 binder 和这几种方式做个简单对比:
-
从性能的角度:数据拷贝的次数,BInder 只需要拷贝一次,而管道,消息队列,Socket 都需要2次,共享内存不许拷贝;所以进次于共享内存
-
从稳定的角度考虑:Binder 基于C/S 架构,也即服务端/客户端的架构。Server 和 Client 相对独立,稳定型比较好;而共享内存实现复杂,需要考虑访问临界资源的并发同步问题;所以 Binder 在这方面要优于共享内存
-
从安全的角度:传统 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 进程空间
首先是几个基础知识:
-
一个进程空间划分为 用户空间 和 内核空间(Kernel), 即把进程内 用户 和 内核隔离开
区别在于:
- 进程间 用户空间的数据不共享,内核空间的数据共享
- 所有进程公用一个内核空间
-
进程间 用户空间和内核空间 进行交互 需通过 系统调用,主要通过函数:
- 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() 函数,实际上创建数据接收的缓存空间。
核心原理就是 内存映射,其工作流程如下:
注册服务
- Server 进程向 Binder 驱动发起服务注册请求
- Binder 驱动将注册请求转发给 ServerManger 进程
- ServerManger 进程添加该 Server 进程(已注册服务)
获取服务
- Client 向Binder 驱动发起获取服务的请求,传递要获取的服务名称
- Binder 驱动将该请求转发给 ServiceManger 进程
- ServiceManger 查找到 Client 需要的 Server 对应的服务信息
- 通过 Binder 驱动将上述服务信息返回给 Client 进程
使用服务
- Binder 驱动为进程间通信做准备:实现内存映射。Binder 驱动 创建一个接收缓存区,根据 Server 信息找打对应的 Server 进程,实现内核缓存区和 Server 进程用户空间地址 同时映射到 同一个接收缓存区中。此时还未传输数据
- Client 进程将参数数据发送到 Server 进程。Client 进程通过系统调用 copy_from_user() 发送数据到内核空间的缓存区(当前线程被挂起),Binder 驱动通知 Server 进程解包。
- Server 进程根据 Client 进程要求 调用目标方法。收到 Binder 驱动通知后,Server 进程从线程池中取出线程,进行数据解包,调用目标方法;将最终执行结果写入到自己的共享内存中。
- 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 机制的模型和通信原理和过程,以备面试