0. 为什么要深入学习 Binder
- Binder 是整个 Android 的基石
- 一切的体系服务都是根据 Binder,比方 AMS WMS PMS SurfaceFlinger Audiofilinger 以及硬件操作服务等等
- Android 四大组件的底层完结离不开 Binder
- 做体系开发需求自定义一些体系服务,这些作业需求咱们了解 Binder
- Android O 以后的 Treble 方案,根据 Binder 魔改出了 HwBinder VndBinder。
- ANR 冻屏 卡顿 卡死等偶现 BUG 可能与 Binder 相关
1. 学习 Binder 的四个阶段
- 会用,能增加 Java Native 体系服务
- 熟读使用层各种情形下的源码
- 熟读内核里面的数据结构和流程
- 能解决各种奇奇怪怪的 bug
2. 准备作业
下载编译好 AOSP + Kernel,能经过自定义内核的办法发动虚拟机。
这部分内容比较简略,能够参阅:
- AOSP 极速上手
- Linux 驱动开发入门第一节
3. 准备基础知识
准备基础知识快速过一遍,忘了再回头再看
- Linux 驱动开发入门
- Linux 内核常用数据结构
- 虚拟内存与 Linux 文件拜访接口
- JNI 编程入门
4. Binder 基本原理
首先要明确一点 Binder 是一个 RPC(Remote Procedure Call) 结构,也便是说借助于 Binder,咱们能够在 A 进程中拜访 B 进程中的函数。
4.1 IPC 原理
RPC 一般根据 IPC 来完结的,IPC 便是跨进程数据传输,大白话便是在 A 进程能够拜访到 B 进程中的数据,或许说 B 进程中的数据能够传递给 A 进程,都是一个意思。
在 Linux 中,每个进程都有自己的虚拟内存地址空间。虚拟内存地址空间又分为了用户地址空间和内核地址空间。
不同进程之间用户地址空间的变量和函数是不能彼此拜访的。
使得 A 进程能拜访到 B 进程中数据的手法咱们就称之为 IPC。
尽管用户地址空间是不能互相拜访的,可是不同进程的内核地址空间是相同和共享的,咱们能够借助内核地址空间作为中转站来完结进程间数据的传输。
具体的咱们在 B 进程运用 copy_from_user 将用户态数据 int a
复制到内核态,这样就能够在 A 进程的内核态中拜访到 int a
更进一步,能够在 A 进程中调用 copy_to_user 能够将 int a
从内核地址空间复制到用户地址空间。至此,咱们的进程 A 用户态程序就能够拜访到进程 B 中的用户地址空间数据 int a
了
为了拜访 int a
,需求复制两次数据。能不能优化一下?咱们能够经过 mmap 将进程 A 的用户地址空间与内核地址空间进行映射,让他们指向相同的物理地址空间:
完结映射后,B 进程只需调用一次 copy_from_user,A 进程的用户空间中就能够拜访到 int a
了。这里就优化到了一次复制。
4.2 RPC 原理
接着咱们来看以下,Binder 的 RPC 是如何完结的:
一般来说,A 进程拜访 B 进程函数,咱们需求:
- 在 A 进程中按照固定的规则打包数据,这些数据包含了:
- 数据发给那个进程,Binder 中是一个整型变量 Handle
- 要调用方针进程中的那个函数,Binder 顶用一个整型变量 Code 表示
- 方针函数的参数
- 要履行具体什么操作,也便是 Binder 协议
- 进程 B 收到数据,按照固定的格局解析出数据,调用函数,并运用相同的格局将函数的返回值传递给进程 A。
Binder 要完结的效果便是,全体上看过去,进程 A 履行进程 B 中的函数就和履行当前进程中的函数是一样的。
5. Binder 使用层作业流程
Binder 是一个 RPC(Remote Procedure Call) 结构,翻译成中文便是长途进程调用。也便是说经过 Binder:
- 能够在 A 进程中拜访 B 进程中定义的函数
- 进程 B 中的这些等待着被长途调用的函数的集合,咱们称其为 Binder 服务(Binder Service)
- 进程 A 称之为 Binder 客户端(Binder Client),进程 B 称之为 Binder 服务端(Binder Server)
- 一般,体系中的服务许多,咱们需求一个管家来办理它们,服务管家(ServiceManager) 是 Android 体系发动时,发动的一个用于办理 Binder 服务(Binder Service) 的进程。一般,服务(Service) 需求事先注册到服务管家(ServiceManager),其他进程向服务管家(ServiceManager) 查询服务后才干运用服务。
- Binder 的 RPC 才能经过 Binder 驱动完结
一般一个完好的 Binder 程序涉及 4 个流程:
- 在 Binder Server 端定义好服务
- 然后向 ServiceManager 注册服务
- 在 Binder Client 中向 ServiceManager 获取到服务
- 建议长途调用,调用 Binder Server 中定义好的服务
整个流程都是建立在 Binder 驱动供给的跨进程调用才能之上:
6. Android Binder 全体架构
从源码完结角度来说,Binder 全体架构完结如下:
有点杂乱,咱们一点点说:
-
VFS 是内核中的一个中间层,向上对使用层供给一致的体系调用函数,这些体系调用函数首要是
open mmap ioctl write read ioctl
等,向下封装不同的外设(字符设备,块设备),体系文件,文件体系的操作。Binder 是一个字符驱动,当使用层调用到 binder 的open mmap ioctl release
体系调用时,经过 vfs 的一层包装后,就会调用到 Binder 驱动中的binder_open bider_mmap binder_ioctl binder_release
函数。 -
不同于一般的驱动,Binder 使用层的运用要杂乱不少,假如直接运用
open mmap ioctl release
体系调用会使得使用程序非常杂乱且难以复用相同功用的代码,刚开端 google 的工程师做了一套简略的封装,把常用的操作封装为一系列的函数,这些函数都在 binder.c 中,ServiceManger 的便是经过 binder.c 中封装的函数完结的(Android10及以前)。源码中还存在一个 bctest.c 的程序,这个是 binder.c 的一个测验程序。C 言语级别的封装尽管简略,但运用起来仍是稍显费事,许多细节也没有考虑进去,所以 google 的工程师又封装了一个叫 libbinder 的库,咱们 native 层的 binder 服务端与客户端都是根据这个库来完结的,Java 层的 binder 服务端与客户端都是经过 JNI 间接运用 libbinder 库完结的,从运用上来说 libbinder 更为简略,可是 libbinder 本身比 binder.c 杂乱了不少。
7. C 层完结剖析
许多博客教程会忽略这一层的剖析,相比 libbinder 库的封装,binder.c 会简略不少 ,方便初学者理解 binder 使用层作业流程。
咱们能够模仿 bctest.c 写一个完好的 Binder 使用层 demo。
这个作业已经有大佬完结了:
github.com/weidongshan…
可是也有一些问题,这个代码是根据 Android5 的,稍微有点老了,我在以上完结的基础上做了一些修改和适配作业,使得代码能够在 Android10 上跑起来:
github.com/yuandaimaah…
关于这个示例程序的剖析,能够参阅以下几篇文章:
- Binder 程序示例之 C 言语篇
- Binder 服务注册进程情形剖析之 C 言语篇
- Binder 服务获取与运用进程情形剖析之 C 言语篇
8. 驱动剖析
驱动剖析这部分结合 C 层使用的完结来剖析驱动的完结,首要搞清楚:
- 三个情形的流程:注册,获取,运用
- 三个情形下内核中各种数据结构的变化
这部分内容能够参阅之前共享的:
- 驱动情形剖析之 ServiceManager 发动进程
- 驱动情形剖析之服务注册进程
- 驱动情形剖析之服务获取与运用进程
9. C++ 层剖析
首先咱们要写一个根据 libbinder 库的 Demo,能跑起来就行:
- Binder 程序示例之 C++ 篇
- Binder 程序示例之 aidl-cpp 篇
接着剖析三个情形下的履行进程与各个函数的功用:
- Binder C++ 程序剖析之首要类解析
- Binder 服务注册进程情形剖析之 C++ 篇
- Binder 服务获取与运用进程情形剖析之 C++ 篇
当然还有两个特别的场景也需求进行剖析:
- 逝世告诉
- 多线程
这部分内容会陆续在大众号和平台推送。
10. Java 层剖析
学习这部分的前提是了解 JNI 编程。这个能够参阅系列文章:
- JNI 编程上手攻略
咱们先写一个 Demo,能跑起来就行:
- Binder 程序示例之 java 篇
接着咱们剖析三个情形下的履行进程与各个函数的功用:
- Binder Java 层初始化
- Binder Java 层服务注册进程剖析
- Binder Java 层服务获取与运用进程剖析
当然还有一些其他高档特性也需求咱们剖析,这部分内容会在后续推送:
- AIDL 中 in out inout oneway 的剖析
- Parcel 数据结构剖析
- Java 层逝世告诉
- Java 层多线程剖析
11. 疑难问题
不论是使用开发仍是体系开发咱们都会遇到一些扎手的 bug,许多时分这些 bug 都和 binder 有关,总结起来,大约能够分为几类:
- 死锁
- 线程池满了
- 代理目标内存走漏
- 传输数据过大
- 要害办法内建议 Binder 同步调用导致卡顿
- Android O 异步长途调用无限阻塞冻屏 bug
这类 bug 许多都难以复现,许多时分都不了了之了,导致具有这部分经历的同学很少。
这部分内容作业量巨大,我会在接下来的时刻陆续在大众号和推送相关的文章。
关于
我叫阿豪,2015 年本科结业于国防科技大学指挥自动化专业,结业后,从事信息化配备的研发作业。首要研究方向为 Android Framework 与 Linux Kernel,2023年春节后开端做 Android Framework 相关的技术共享。
假如你对 Framework 感兴趣或许正在学习 Framework,能够参阅我总结的Android Framework 学习路线攻略,也可关注我的微信大众号,我会在大众号上继续共享我的经历,帮助正在学习的你少走一些弯路。学习进程中假如你有疑问或许你的经历想要共享给大家能够增加我的微信,我拉你进技术交流群。