Binder机制——Android(保姆级核心版)全部都要记住


阅读前言:欢迎评论、一起交流学习,如有错误及不足,望海涵,如这篇文章对您有用,麻烦帮忙点点小蛋糕,谢谢您的喜欢~


明确:Android是基于Linux实现的

明确:在Android中使用Activity、Service等组件需要和AMS(ActivityManagerServer)进行通信,这种跨进程的通信都是通过Binder完成的

明确:Activity、Service等组件和AMS不是同一个进程,所以为多进程通信

1.为什么Android要采用Binder?

因为Linux的通信机制不适用于Android,Android需要高效、安全、一对多的通信设计模式

解释:为什么Linux不满足呢?
我们由此需要了解Linux进程通信有哪些,并逐一分析它们的缺点

进程的本质:占据内存的资源

进程通信的本质:内存拷贝

首先: Linux通信包含(管道通信、共享内存、socket、File)

(1)管道通信

性能:需要拷贝2次

安全:正常、一般

通信设计模式:1对1 (这一点特别重要,根本上就不能成为Android通信机制)

(2)内存共享

性能:不需要拷贝

安全:不安全  (大家都可以互相访问的话......那...太简单粗暴了)(pass关键)

通信设计模式:n对n(也被pass,不能成为Android通信机制)

进程ABCD都可以共享Person

(3)socket

性能:需要拷贝2次,效率低

安全:不安全 (pass关键)

通信设计模式:1对n (优点:和Binder一样)

(4)File:和管道通信差不多,不在此介绍了

而Binder机制(优势)

性能:需要拷贝1次,效率高

安全:由Binder内核展开,很安全,为每个APP分配UID

通信设计模式:1对n

特点:基于C/S架构,易用性高

2.Binder是如何做到一次拷贝的?

需要明确:用户空间与内核空间

用户空间与内核空间
Linux传统管道通信:用户空间与内核空间

 需要明确:物理地址与虚拟地址

(1)MMU:内存管理单元  功能:进行虚拟地址到物理地址的转换

传统设备:CPU与内存芯片直接相连

传统设备:CPU与内存芯片直接相连

 现代设备:CPU与内存芯片之间相连要经过MMU

现代设备:CPU与内存芯片之间相连要经过MMU

(2)现代设备以来MMU之前的为虚拟地址,之后的为物理地址

(3)MMU调节活跃代码与不活跃代码,活跃代码放入内存,不活跃代码放入磁盘

(4)MMU同时维护一个页表,将虚拟地址与物理地址建立联系

举个例子执行代码:1.int a;2.a = 10

第一步:CPU执行代码int a;判断内存是否开辟a空间

第二步:没有 MMU就去磁盘读取int a代码

第三步:MMU拿到磁盘读出的int a代码

第四步:去物理内存开辟a的空间

第五步:同时创建a的物理地址

第六步:页表保存开辟出来的a的地址

第七步:根据a的物理地址随机创建一个虚拟地址

第八步:CPU拿到虚拟地址

第九步:执行代码a = 10

第十步:根据a找到其虚拟地址

第十一步:在找到其物理地址

第十二步:在物理内存中赋值10

需要明确:虚拟地址不能存数据,只能在物理内存中存


那么如何去开辟物理内存存数据呢?

举例:A、B进程间要进行通信,进程B是service端需要开辟物理内存空间(如图)

开辟物理空间时序图如下:

第一步:所有APP从zygote启动,zygote进程启动时调用app_main.cpp(入口)

第二步:在app_main.cpp中调用onzygoteinit()方法中的self()方法进入ProcessState.cpp中

第三步:在ProcessState.cpp中调用self()方法中的new方法,构造对象new ProcessState()

第四步:进入ProcessState的构造方法中打开驱动(返回磁盘Binder路径fd),赋值给mDriverFD

第五步:如果打开成功(mDriver>=0),说明文件路径存在,调用mmap函数,将文件映射到内存(物理内存),开辟物理空间    返回虚拟地址mVMStart

以上五步为开辟物理内存流程


下面详细介绍mmap()函数参数:

mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

0:物理内存开始的地址—— 0即为随机系统进行分配

BINDER_VM_SIZE:开辟物理内存的大小,最大为1M-8K,不足一页(4K),按一页处理

PROT_READ:协议 可读/可写

MAP_PRIVATE | MAP_NORESERVE:映射类型能否共享,即进程A开辟物理空间后,进程B、C、D能否读取使用

mDriverFD:Binder的系统文件路径,将物理内存和磁盘数据连接了

用来将活跃代码写入内存,将不活跃代码写入磁盘    而且通过此地址可以找到物理地址之前映射的区域

0:偏移量 0即为不偏移


引申:Activity传递的参数受到什么的限制?最大传输量是?

收到mmap开辟物理空间的大小限制,最大传输1M-8K,即Activity进行进程通信一次最多拷贝1M-8K数据

Binder是用来通信的,不适合用来传输文件(更不适用于传bitmap),通信数据量本身就很小,所以是1M


返回的虚拟地址有什么用呢?

虚拟地址mVMstart存储在内核空间当中,进程A需要使用copy_from_user将数据从客户端通过内核空间中的虚拟地址mVMstart在页表找到物理地址从而复制数据到物理内存,而这里的物理内存就是进程B通过mmap函数开辟的,所以B进程可以在物理内存中拿到数据啦  (反复看,去理解)

这里是Binder进行一次拷贝的本质


总结Binder如何做到一次拷贝的:

​​​​​​

(1)进程A、B开始通信传数据啦

(2)进程B通过mmap函数开辟物理内存空间

(3)mmap返回一个叫mVMstart的虚拟地址放到内核进程(内核空间)中

(4)进程A通过虚拟地址找到物理地址把数据复制到物理内存

(5)进程B直接拿取物理内存空间的数据

(6)传输数据成功!奖励一块小蛋糕!


3.Binder机制是如何跨进程的?

假设你(Client 进程)想要去图书馆(Server 进程)借书。但是图书馆不能随便进,有一个图书馆管理员(Service Manager)专门负责管理这件事

1. Server 端注册
图书馆(Server 进程)在图书馆管理员(Service Manager)那里登记了自己的信息,比如图书馆的名字(唯一的服务标识),并且告诉管理员这里有很多书(创建了 Binder 对象,代表图书馆提供的服务)。这样,图书馆管理员就知道有这么一个图书馆可以提供借书服务了

2. Client 端获取服务代理
你(Client 进程)想去借书,就跑到图书馆管理员(Service Manager)那里,跟管理员说你要去那个特定名字的图书馆借书。管理员就给你一张 “借书通行证”(Binder 代理对象)。这张通行证看起来就像你自己的东西(在 Client 进程中看起来像本地对象),但实际上它是用来让你进入图书馆借书的凭证,代表着图书馆那边的服务

3. 跨进程通信

第一步:封装数据    你拿着 “借书通行证” 准备去借书,你得先想好你要借什么书(方法名),书的作者、出版社等信息(参数)。然后你把这些信息写在一张纸条上(封装成 Parcel 对象),这张纸条就包含了你借书的所有要求

第二步:发送请求    你把这张纸条交给门口的保安(Binder 驱动),保安会把纸条从你手里(Client 进程的地址空间)拿到他那里(内核空间),然后再把纸条送到图书馆里面(Server 进程的地址空间)

第三步:Server 端接收请求并处理    图书馆里面有很多工作人员(Binder 线程池中的线程),他们收到了保安送过来的纸条,打开一看(解析请求),知道你要借什么书了。然后他们就去书架上找你要的书(调用实际 Binder 对象的相应方法,执行具体业务逻辑)

第四步:返回结果    工作人员找到书后,把书的相关信息(处理结果)也写在一张纸条上(封装成 Parcel 对象),再交给保安。保安又把这张纸条从图书馆里面(Server 进程)拿出来,送到你手里(Client 进程)。你拿到纸条后,一看就知道你要借的书找到了,或者没找到等具体情况(代理对象解析并返回给调用者)


补充内容:

服务端重写onBind()方法,此方法内实例化aidl,生成接口,该接口中有两个内部类Stub、Proxy,Stub用来接受数据,Proxy用来发送数据

调用端调用函数,进入proxy()函数发送参数,再序列化参数(字符串、字节),以序列化的形式写入,再调用transact()方法,直接走到驱动层的binder_transaction()方法,此方法的代码都运行在内核空间中,在此方法内内核空间调用copy_from_user()方法

binder在调用端拷贝几次?1次

binder在调用端传递参数实际拷贝几次?2次  两次copy_from_user,两次都必须会执行,第一次调用传递数据,第二次调用传递请求头。请求头大小为8K。请求头包含信息:哪个类、目标进程,目的是为了可以确定传递方向以及回调数据方向


4.Android APP有多少Binder进程?

15个,超出15个就要进行等待执行

5.Intent传参,异步情况下,数据量是多大?

同步执行:

Intent intent = new Intent(this, SecondActivity.class);

intent.putExtra("", "");

startActivity();

异步执行:

将上段代码放入一个线程中执行

new Thread() {

    @Override

    public void run() {

        Intent intent = new Intent(MainActivity.this, SecondActivity.class);

        intent.putExtra("", new byte[(1 * 1024 * 1024 - 8 * 1024) / 2]);

        startActivity(intent);

    }

}.start();

数据量为同步的一半:即(1M-8K) / 2

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值