Framework源码面试六部曲:2.Binder通信机制
Posted 初一十五啊
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Framework源码面试六部曲:2.Binder通信机制相关的知识,希望对你有一定的参考价值。
一丶前言
今天在电脑上翻出了很久之前整理笔记Framework
源码面试,Flutter
,以及一部分面试专题。拿出来温习一下。
今天先讲Framework
源码篇:
1.
Framework
源码面试:1.Activity
启动流程
2.Framework
源码面试:2.Binder
面试
3.Framework
源码面试:3.Handler
面试
4.Framework
源码面试:4.事件分发机制
5.Framework
源码面试:5.onMeasure
测量原理
6.Framework
源码面试:6.android
屏幕刷新机制
二丶为什么要用binder
-
Android系统内核是
Linux
内核 -
Linux
内核进程通信有:管道、内存共享、Socket
、File
; -
对比:
Binder
的一次拷贝发生在用户空间拷贝到内核空间;
用户空间: App
进程运行的内存空间;
内核空间: 系统驱动、和硬件相关的代码运行的内存空间,也就是进程ID为0的进程运行的空间;
程序局部性原则: 只加载少量代码;应用没有运行的代码放在磁盘中,运行时高速缓冲区进行加载要运行的代码;默认一次加载一个页(4K),若不够4K就用0补齐;
MMU
:内存管理单元;
给CPU
提供虚拟地址;
当对变量操作赋值时:
-
CPU
拿着虚拟地址和值给到MMU
-
MMU
用虚拟地址匹配到物理地址,MMU
去物理内存中进行赋值;
物理地址: 物理内存的实际地址,并不是磁盘;
虚拟地址: MMU
根据物理内存的实际地址翻译出的虚拟地址;提供给CPU
使用;
页命中:CPU
读取变量时,MMU
在物理内存的页表中找到了这个地址;
页未命中:CPU
读取变量时,MMU
在物理内存的页表中没有找到了这个地址,此时会触发MMU
去磁盘读取变量并存到物理内存中;
普通的二次拷贝:
应用A拷贝到服务端:coay_from_user
从服务端拷贝到应用B:coay_to_user
mmap():
-
在物理内存中开辟一段固定大小的内存空间
-
将磁盘文件与物理内存进行映射(理解为绑定)
-
MMU
将物理内存地址转换为虚拟地址给到CPU
(虚拟地址映射物理内存)
共享内存进程通信:
-
进程A调用
mmap()
函数会在内核空间中虚拟地址和一块同样大小的物理内存,将两者进行映射 -
得到一个虚拟地址
-
进程B调用
mmap()
函数,传参和步骤1一样的话,就会得到一个和步骤2相同的虚拟地址 -
进程A和进程B都可以用同一虚拟地址对同一块映射内存进行操作
-
进程A和进程B就实现了通信
-
没有发生拷贝,共享一块内存,不安全
Binder通信原理:
角色:Server端A、Client端B、Binder驱动、内核空间、物理内存
-
Binder驱动在物理内存中开辟一块固定大小(1M-8K)的物理内存w,与内核空间的虚拟地址x进行映射得到
-
A的用户空间的虚拟地址ax和物理内存w进行映射
-
此时内核空间虚拟地址x和物理内存w已经进行了映射,物理内存w和Server端A的用户空间虚拟地址ax进行了映射:也就是 内核空间的虚拟地址x = 物理内存w = Server端A的用户空间虚拟地址ax
-
B发送请求:将数据按照binder协议进行打包给到Binder驱动,Binder驱动调用
coay_from_user()
将数据拷贝到内核空间的虚拟地址x -
因步骤3中的三块区域进行了映射
-
Server端A就得到了Client端B发送的数据
-
通过内存映射关系,只发生了一次拷贝
Activity
跳转时,最多携带1M-8k(1兆减去8K)的数据量;
真实数据大小为:1M内存-两页的请求头数据=1M-8K;
应用A直接将数据拷贝到应用B的物理内存空间中,数据量不能超过1M-8K;拷贝次数少了一次,少了从服务端拷贝到用户;
IPC通信机制:
-
服务注册
-
服务发现
-
服务调用
以下为简单的主进程和子进程通信:
1、服务注册: 缓存中心中有三张表(暂时理解为三个HashMap
,Binder
用的是native
的红黑树):
-
第一种:放
key
:String - value
:类的Class
; -
第二种:放
key
:Class
的类名 - value:类的方法集合; -
第三种:放
key
:Class
的类名 - value:类的对象;
类的方法集合:key-value
;
key:方法签名:“方法名” 有参数时用 “方法名-参数类型-参数类型-参数类型…”;
value
: 方法本身;
注册后,服务若没被调用则一直处于沉默状态,不会占用内存,这种情况只是指用户进程里自己创建的服务,不适用于AMS这种;
2、服务发现: 当被查询到时,要被初始化;
-
客户端B通过发送信息到服务端A
-
服务端解析消息,反序列化
-
通过反射得到消息里的类名,方法,从注册时的第一种、第二种表里找到Class,若对象没初始化则初始化对象,并将对象添加到第三种的表里;
3、服务调用:
-
使用了动态代理
-
客户端在服务发现时,拿到对象(其实是代理)
-
客户端调用对象方法
-
代理发送序列化数据到服务端A
-
服务端A解析消息,反序列化,得到方法进行处理,得到序列化数据结果
-
将序列化结果写入到客户端进程的容器中;
-
回调给客户端
AIDL
: BpBinder
:数据发送角色 BbBinder:数据接收角色
编译器生成的AIDL
的java接口.Stub.proxy.transact()
为数据发送处;
发送的数据包含:数据+方法code+方法参数等等;
-
发送时调用了
Linux
的驱动 -
调用
copy_from_user()
拷贝用户发送的数据到内核空间 -
拷贝成功后又进行了一次请求头的拷贝:
copy_from_user()
-
也就是把一次的数据分为两次拷贝
请求头:包含了目的进程、大小等等参数,这些参数占了8K
编译器生成的AIDL
的java接口.Stub.onTransact()
为数据接收处;
Binder
中的IPC
机制:
-
每个App进程启动时会在内核空间中映射一块1M-8K的内存
-
服务端A的服务注册到
ServiceManager
中:服务注册 -
客户端B想要调用服务端A的服务,就去请求
ServiceManager
-
ServiceManager
去让服务端A实例化服务:服务发现 -
返回一个用来发送数据的对象
BpBinder
给到客户端B -
客户端B通过
BpBinder
发送数据到服务端A的内核的映射区域(传参时客户端会传一个reply序列化对象,在底层会将这个地址一层一层往下传,直至传到回调客户端):这里发生了一次通信copy_from_user
:服务调用 -
服务端A通过
BBBinder
得到数据并处理数据 -
服务端唤醒客户端等待的线程;将返回结果写入到客户端发送请求时传的一个
reply
容器地址中,调用onTransact
返回; -
客户端在
onTransac
中得到数据;通信结束;
ServiceManager
维持了Binder
这套通信框架;
三丶APP多进程的优点
- 扩大应用可使用的内存
手机内存6G,系统分配给虚拟机的内存一般32M、48M、64M,使用多进程时,可以使用一个进程专门加载图片,防止OOM
。 - 子进程崩溃,不会导致主进程崩溃
- 互相保活,即如果子进程被系统kill掉时,主进程拉起子进程。主进程被系统kill掉时,子进程拉起主进程。
四丶多进程通信原理
Android进程是运行在系统分配的虚拟地址空间,虚拟地址空间分为用户空间和内核空间。多进程间,用户空间不共享,内核空间共享,进程间通过共享的内核空间通信。
五丶多进程通信有哪些方式?
1.传统的IPC
方式:socket
,内存共享。
2.Android特有的方式:Binder
。
六丶Binder
相对其他IPC
方式优点/为什么使用Binder
?
1.性能:
A.Socket
传输数据的过程:两次拷贝
B.Binder
传输数据的过程:一次拷贝
内存映射:MMAP
(memory map
)
虚拟内存和物理内存
虚拟内存映射到物理内存,物理内存存储数据。
2.易用性
3.安全性
七丶Binder在Android系统CS通信机制中起到的作用
-
Android C/S
通信机制
-
Binder
机制的关键概念
-
Binder
在Android CS
通信机制中起到的作用
AIDL
和Binder
的关系?
AIDL
封装了Binder
,AIDL
调用Binder
以上是关于Framework源码面试六部曲:2.Binder通信机制的主要内容,如果未能解决你的问题,请参考以下文章
Framework源码面试六部曲:3.Handler面试集合
Framework源码面试六部曲:5.onMeasure测量原理
Framework源码面试六部曲:1.activity启动流程