上次没砍我的,这次我又来了;看完这篇还不明白 Binder 你砍我

最近一段时间由于工作,接触到 Framework 部分比较多一点,也难免要和 Binder 打一些交道, 因为在 Android 系统中,每一个应用程序都是由一些 ActivityService 组成的,这些 ActivityService 有可能运行在同一个进程中,也有可能运行在不同的进程中;那么,不在同一个进程的 Activity 或者 Service 是如何通信的呢?这就是本文中要介绍的 IPC 机制中的 Binder 进程间通信机制

首先来了解下什么是 IPC 机制

Linux 中,是以进程单位分配和管理资源的;出于保护机制,一个进程不能直接访问另一个进程的资源,也就是说,进程之间互相封闭;但是,一个复杂的应用系统中,通常会使用多个相关的进程来共同完成一项任务,因此要求进程之间必须能够互相通信,从而共享资源和信息;所以,操作系统内核必须提供进程间的通信机制(IPC)

IPC 机制种类

  • 采用命名管道(name pipe)
  • 消息队列(message queue)
  • 信号(signal)
  • 内存共享(share memory)

但在 Android 终端上的应用软件的通信几乎看不到这些 IPC 通信方式,取而代之的是 Binder 方式

什么是 Binder ?

Binder 是 Android 系统中最重要的特性之一;正如其名**“粘合剂”,它是粘合系统间各个组件的桥梁,Android 系统的开放式设计也很大程度上得益于这种跨进程通信机制**

所以理解 Binder 对于理解整个 Android 系统有着非常重要的作用,Android 系统的四大组件AMS,PMS系统服务无一不与 Binder 挂钩;如果对Binder 不甚了解,那么就很难了解这些系统机制,从而仅仅浮与表面,不懂 Binder 你都不好意思说自己会 Android 开发;想要深入 Android,Binder 必须迈出的一步

Binder 进程通讯

应用程序虽然是以独立的进程来运行的,但相互之间还是需要通信,比如,在多进程的环境下,应用程序后台服务通常会运行在不同的进程中,有着独立的地址空间,但是因为需要相互协作,彼此间又必须进行通信和数据共享,这就需要进程通信来完成

进程间通信的方式

Linux 系统中有:

  • socket
  • named pipe
  • message queue
  • signal
  • share
  • memory

Java 系统中也有:

  • socket
  • namedpipe

所以 Android 可以选择的进程间通信的方式也很多,但是它主要包括以下几种方式:

  • 标准 Linux Kernel IPC 接口
  • 标准 D-BUS 接口
  • Binder 接口

为什么选择 Binder ?

在上面这些可供选择的方式中,Android 使用得最多也最被认可的还是 Binder 机制;为什么会选择Binder来作为进程之间的通信机制呢?

实则是因为 Binder 更加简洁和快速消耗的内存资源更小吗?不错,这些也正是 Binder优点;当然,也还有很多其他原因,比如传统的进程间通信可能会增加进程的开销,而且有进程过载安全漏洞等方面的风险,Binder 正好能解决和避免这些问题;Binder主要能提供以下一些功能:

  • 用驱动程序来推进进程间的通信
  • 通过共享内存来提高性能
  • 为进程请求分配每个进程的线程池
  • 针对系统中的对象引入了引用计数和跨进程的对象引用映射
  • 进程间同步调用

再来看看 Binder 的线程管理

Binder 线程池的概念

  • 每个 Server 进程在启动时会创建一个 binder 线程池,并向其中注册一个 Binder 线程;之后 Server 进程也可以向 binder 线程池注册新的线程,或者 Binder 驱动在探测到没有空闲 binder 线程时会主动向Server进程注册新的的 binder 线程
  • 对于一个Server进程有一个最大Binder线程数限制,默认为16个binder线程,例如Android的system_server进程就存在16个线程。对于所有Client端进程的binder请求都是交由Server端进程的binder线程来处理的

Binder 实际上是位于不同进程中的线程之间的通信

  • 假如进程 S 是 Server 端,提供 Binder 实体,线程 T1 从 Client 进程 C1 中通过 Binder 的引用向进程 S 发送请求
  • S 为了处理这个请求需要启动线程 T2,而此时线程 T1 处于接收返回数据的等待状态。T2 处理完请求就会将处理结果返回给 T1,T1 被唤醒得到处理结果
  • 在这过程中,T2 仿佛 T1 在进程S中的代理,代表T1执行远程任务,而给T1的感觉就是象穿越到S中执行一段代码又回到了 C1
  • 为了使这种穿越更加真实,驱动会将 T1 的一些属性赋给 T2,特别是 T1 的优先级 nice,这样 T2 会使用和 T1 类似的时间完成任务
  • 很多资料会用**‘线程迁移’来形容这种现象,容易让人产生误解一来线程根本不可能在进程之间跳来跳去,二来T2除了和T1优先级一样,其它没有相同之处,包括身份,打开文件,栈大小,信号处理,私有数据**等
Binder 线程与 Binder 主线程的区别
  • 线程是否可以终止 Loop,不过目前启动的 Binder 线程都是无法退出的,其实可以全部看做是 Binder 主线程
  • 其实现原理是,在 SystemServer 主线程执行到最后的时候,Loop 监听 Binder 设备,变身死循环线程

关于工作线程的启动,Binder 驱动还做了一点小小的优化

  • 当进程 P1 的线程 T1 向进程 P2 发送请求时,驱动会先查看一下线程 T1 是否也正在处理来自 P2 某个线程请求但尚未完成(没有发送回复);这种情况通常发生在两个进程都有 Binder 实体并互相对发时请求时

  • 假如驱动在进程 P2中 发现了这样的线程,比如说 T2,就会要求 T2 来处理 T1 的这次请求;因为T2既然向 T1 发送了请求尚未得到返回包,说明 T2 肯定(或将会)阻塞在读取返回包的状态;这时候可以让 T2 顺便做点事情,总比等在那里闲着好;而且如果 T2 不是线程池中的线程还可以为线程池分担部分工作,减少线程池使用率

  • Binder 线程是 执行 Binder 服务的载体,只对于服务端才有意义,对请求端来说,是不需要考虑 Binder 线程的;所以 Binder 线程就是执行 Binder 实体业务的线程

今天有关于 Framework IPC 机制中的 Binder 进程间通信机制的阐述就到这里了;为了帮助大家了解更多 Android Framework 框架层 必备的技术知识,这里特别提供一份由腾讯大佬所整理的一张 Framework 思维导图及其配套的一份学习手册;有需要这份思维导图及学习手册 的朋友: 可以私信发送 “架构图”“进阶” 即可 直达获取;希望大家看完之后能给大家一些帮助

内容展示如下:

Android Framework 思维导图

高清版 Android Framework 思维导图 获取方式:私信发送 “架构图” 即可 直达获取

应用程序与 AMS 的通讯实现

  • 从应用程序进程到管理者进程
  • 应用程序进程向管理者进程发送消息
  • 从管理者进程到应用程序进程
  • 管理者进程向应用程序进程发送消息
  • 用户进程接收消息

完整版 Android Framework 思维导图及学习手册 获取方式: 私信发送 “架构图” 或 “进阶” 即可 直达获取

应用进程与 WMS 的通讯实现

  • WindowManagerImpl & WindowManagerGlobal
  • ViewRootImpl
  • 从应用进程到管理者进程
  • 从管理者进程到应用进程

应用进程之间的通讯实现

  • 服务端编写 AIDL 文件
  • 编写 Service
  • 声明 Service
  • 客户端编写 AIDL 文件
  • 绑定服务,并调用
  • IBinder实现原理

完整版 Android Framework 思维导图及学习手册 获取方式: 私信发送 “架构图” 或 “进阶” 即可 直达获取

既然选择了程序员这个行业,那么你一定要做好充足的准备;要想在人前显贵,背后所付出的辛劳和汗水就是必须的

Android 架构师之路还能漫长,与君共勉

猜你喜欢

转载自blog.csdn.net/m0_70748845/article/details/126977580