02.Android之IPC机制问题

37次阅读

共计 9608 个字符,预计需要花费 25 分钟才能阅读完成。

目录介绍

2.0.0.1 什么是 Binder?为什么要使用 Binder?Binder 中是如何进行线程管理的?总结 binder 讲的是什么?
2.0.0.2 Android 中进程和线程的关系?什么是 IPC?为何需要进行 IPC?多进程通信可能会出现什么问题?
2.0.0.3 Binder 的工作流程是怎样的?Binder 主要能提供哪些功能?Binder 通信机制原理是怎样的?
2.0.0.4 Android 中为何新增 Binder 来作为主要的 IPC 方式?Binder 运行机制是怎样的?Binder 机制有什么优势?
2.0.0.5 Android 中跨进程通讯的几种方式?实际开发中,有哪些场景使用 Binder 进行数据传输?
2.0.0.6 Android 中有哪些基于 Binder 的 IPC 方式?简单对比下?
2.0.0.7 为何说 Binder 相比传统的 Socket 性能更高效?为何说 Binder 相比传统 IPC 安全性更高?
2.0.0.8 Service Manager 是如何成为一个守护进程的?Server 和 Client 是如何获得 Service Manager 接口的?

好消息

博客笔记大汇总【15 年 10 月到至今】,包括 Java 基础及深入知识点,Android 技术博客,Python 学习笔记等等,还包括平时开发中遇到的 bug 汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是 markdown 格式的!同时也开源了生活博客,从 12 年起,积累共计 500 篇 [近 100 万字],将会陆续发表到网上,转载请注明出处,谢谢!
链接地址:https://github.com/yangchong2…
如果觉得好,可以 star 一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!所有的笔记将会更新到 GitHub 上,同时保持更新,欢迎同行提出或者 push 不同的看法或者笔记!

2.0.0.1 什么是 Binder?为什么要使用 Binder?Binder 中是如何进行线程管理的?总结 binder 讲的是什么?

什么是 Binder?

1. 直观来说,Binder 是 Android 中的一个类,它继承了 IBinder 接口
2. 从 IPC 角度来说,Binder 是 Android 中的一种跨进程通信方式,Binder 还可以理解为一种虚拟的物理设备,它的设备驱动是 /dev/binder,该通信方式在 linux 中没有
3. 从 Android Framework 角度来说,Binder 是 ServiceManager 连接各种 Manager(ActivityManager、WindowManager,etc)和相应 ManagerService 的桥梁
4. 从 Android 应用层来说,Binder 是客户端和服务端进行通信的媒介,当你 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于 AIDL 的服务。

为什么要使用 Binder?技术博客大总结

在传统的 Linux 上,我们还是有很多选择可以用来实现进程间通信,如管道、SystemV、Socket 等。那么 Android 为什么不使用这些原有的技术,而是要使开发一种新的叫 Binder 的进程间通信机制呢?
最简单的回答:性能:相比传统的 Socket 更高效;安全:安全性高,支持通信双方进行身份验证。

详细一点说,主要有两个方面的原因:

性能方面
在移动设备上(性能受限制的设备,比如要省电),广泛地使用跨进程通信对通信机制的性能有严格的要求,Binder 相对出传统的 Socket 方式,更加高效。Binder 数据拷贝只需要一次,而管道、消息队列、Socket 都需要 2 次,共享内存方式一次内存拷贝都不需要,但实现方式又比较复杂。

安全方面
传统的进程通信方式对于通信双方的身份并没有做出严格的验证,比如 Socket 通信 ip 地址是客户端手动填入,很容易进行伪造,而 Binder 机制从协议本身就支持对通信双方做身份校检,因而大大提升了安全性。

还有一些好处,如实现面象对象的调用方式,在使用 Binder 时就和调用一个本地实例一样。

Binder 中是如何进行线程管理的?

每个 Binder 的 Server 进程会创建很多线程来处理 Binder 请求,可以简单的理解为创建了一个 Binder 的线程池吧(虽然实际上并不完全是这样简单的线程管理方式),而真正管理这些线程并不是由这个 Server 端来管理的,而是由 Binder 驱动进行管理的。
一个进程的 Binder 线程数默认最大是 16,超过的请求会被阻塞等待空闲的 Binder 线程。理解这一点的话,你做进程间通信时处理并发问题就会有一个底,比如使用 ContentProvider 时(又一个使用 Binder 机制的组件),你就很清楚它的 CRUD(创建、检索、更新和删除)方法只能同时有 16 个线程在跑。

总结 binder 讲的是什么?

通常意义上来说,Binder 就是指 Andriod 的通信机制;
对于服务端进程来说,Binder 指的是 Binder 本地对象,对于客户端进程来说,Binder 指的是 Binder 代理对象。
对于传输过程来说,Binder 是可以进行跨进程传递的对象;

2.0.0.2 Android 中进程和线程的关系?什么是 IPC?为何需要进行 IPC?多进程通信可能会出现什么问题?

Android 中进程和线程的关系?

一个 APP 一般对应一个进程和有限个线程

一般对应一个进程,当然,可以在 AndroidMenifest 中给四大组件指定属性 android:process 开启多进程模式
有限个线程:线程是一种受限的系统资源,不可无限制的产生且线程的创建和销毁都有一定的开销。

什么是 IPC?

为何需要进行 IPC?

进程间通信的必要性
所有运行在不同进程的四大组件,只要它们之间需要通过内存在共享数据,都会共享失败。这是由于 Android 为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类的对象会产生多份副本。技术博客大总结

多进程造成的影响可总结为以下四方面

静态变量和单例模式失效:由独立的虚拟机造成
线程同步机制失效:由独立的虚拟机造成
SharedPreference 的不可靠下降:不支持两个进程同时进行读写操作,即不支持并发读写,有一定几率导致数据丢失
Application 多次创建:Android 系统会为新的进程分配独立虚拟机,相当于系统又把这个应用重新启动了一次。

2.0.0.3 Binder 的工作流程是怎样的?Binder 主要能提供哪些功能?Binder 通信机制原理是怎样的?

Binder 的工作流程是怎样的?

1 客户端首先获取服务器端的代理对象。所谓的代理对象实际上就是在客户端建立一个服务端的“引用”,该代理对象具有服务端的功能,使其在客户端访问服务端的方法就像访问本地方法一样。
2 客户端通过调用服务器代理对象的方式向服务器端发送请求。
3 代理对象将用户请求通过 Binder 驱动发送到服务器进程。
4 服务器进程处理用户请求,并通过 Binder 驱动返回处理结果给客户端的服务器代理对象。
5 客户端收到服务端的返回结果。

binder 工作流程图如下所示:

Binder 主要能提供哪些功能?

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

Binder 通信机制原理是怎样的?

Server 进程向 ServiceManager 注册,告诉 ServiceManager 我是谁,我有什么,我能做什么。就好比徐同学(Server 进程)有一台笔记本(computer 对象),这台笔记本有个 add 方法。这时映射关系表就生成了。技术博客大总结

Client 进程向 ServiceManager 查询,我要调用 Server 进程的 computer 对象的 add 方法,可以看到这个过程经过 Binder 驱动,这时候 Binder 驱动就开始发挥他的作用了。当向 ServiceManager 查询完毕,是返回一个 computer 对象给 Client 进程吗?其实不然,Binder 驱动将 computer 对象转换成了 computerProxy 对象,并转发给了 Client 进程,因此,Client 进程拿到的并不是真实的 computer 对象,而是一个代理对象,即 computerProxy 对象。很容易理解这个 computerProxy 对象也是有 add 方法,(如果连 add 方法都没有,岂不是欺骗了 Client?),但是这个 add 方法只是对参数进行一些包装而已。
当 Client 进程调用 add 方法,这个消息发送给 Binder 驱动,这时驱动发现,原来是 computerProxy,那么 Client 进程应该是需要调用 computer 对象的 add 方法的,这时驱动通知 Server 进程,调用你的 computer 对象的 add 方法,将结果给我。然后 Server 进程就将计算结果发送给驱动,驱动再转发给 Client 进程,这时 Client 进程还蒙在了鼓里,他以为自己调用的是真实的 computer 对象的 add 方法,其实他只是调用了代理而已。不过 Client 最终还是拿到了计算结果。

2.0.0.4 Android 中为何新增 Binder 来作为主要的 IPC 方式?Binder 运行机制是怎样的?Binder 机制有什么优势?

Binder 运行机制是怎样的?

Binder 基于 Client-Server 通信模式,除了 Client 端和 Server 端,还有两角色一起合作完成进程间通信功能。

Binder 通信的四个角色:

Client 进程:使用服务的进程。
Server 进程:提供服务的进程。
ServiceManager 进程:ServiceManager 的作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 名字获得对 Server 中 Binder 实体的引用。
Binder 驱动:驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

接触这些概念可能会觉得难于理解,读者可以把四个角色和熟悉的互联网进行类比:Server 是服务器,Client 是客户终端,ServiceManager 是域名服务器(DNS),驱动是路由器。

Binder 机制有什么优势

传输效率高、可操作性强:传输效率主要影响因素是内存拷贝的次数,拷贝次数越少,传输速率越高。从 Android 进程架构角度分析:对于消息队列、Socket 和管道来说,数据先从发送方的缓存区拷贝到内核开辟的缓存区中,再从内核缓存区拷贝到接收方的缓存区,一共两次拷贝
技术博客大总结
对于 Binder 来说,数据从发送方的缓存区拷贝到内核的缓存区,而接收方的缓存区与内核的缓存区是映射到同一块物理地址的,节省了一次数据拷贝的过程
由于共享内存操作复杂,综合来看,Binder 的传输效率是最好的。
实现 C / S 架构方便:Linux 的众 IPC 方式除了 Socket 以外都不是基于 C / S 架构,而 Socket 主要用于网络间的通信且传输效率较低。Binder 基于 C / S 架构,Server 端与 Client 端相对独立,稳定性较好。
安全性高:传统 Linux IPC 的接收方无法获得对方进程可靠的 UID/PID,从而无法鉴别对方身份;而 Binder 机制为每个进程分配了 UID/PID 且在 Binder 通信时会根据 UID/PID 进行有效性检测。

2.0.0.5 Android 中跨进程通讯的几种方式?实际开发中,有哪些场景使用 Binder 进行数据传输?

Android 中跨进程通讯的几种方式?

Android 跨进程通信,像 intent,contentProvider, 广播,service 都可以跨进程通信。

intent:这种跨进程方式并不是访问内存的形式,它需要传递一个 uri, 比如说打电话。
contentProvider:这种形式,是使用数据共享的形式进行数据共享。
service:远程服务,aidl
广播技术博客大总结

实际开发中,有哪些场景使用 Binder 进行数据传输?

通过 AIDL 实现方式解释 Binder 数据传输的具体过程
服务端中的 Service 给与其绑定的客户端提供 Binder 对象,客户端通过 AIDL 接口中的 asInterface() 将这个 Binder 对象转换为代理 Proxy,并通过它发起 RPC 请求。客户端发起请求时会挂起当前线程,并将参数写入 data 然后调用 transact(),RPC 请求会通过系统底层封装后由服务端的 onTransact() 处理,并将结果写入 reply,最后返回调用结果并唤醒客户端线程。

AIDL 原理是什么?如何优化多模块都使用 AIDL 的情况?

AIDL(Android Interface Definition Language,Android 接口定义语言):如果在一个进程中要调用另一个进程中对象的方法,可使用 AIDL 生成可序列化的参数,AIDL 会生成一个服务端对象的代理类,通过它客户端实现间接调用服务端对象的方法。
AIDL 的本质是系统提供了一套可快速实现 Binder 的工具。关键类和方法:

AIDL 接口:继承 IInterface。
Stub 类:Binder 的实现类,服务端通过这个类来提供服务。
Proxy 类:服务器的本地代理,客户端通过这个类调用服务器的方法。
asInterface():客户端调用,将服务端的返回的 Binder 对象,转换成客户端所需要的 AIDL 接口类型对象。返回对象:
若客户端和服务端位于同一进程,则直接返回 Stub 对象本身;
否则,返回的是系统封装后的 Stub.proxy 对象。
asBinder():根据当前调用情况返回代理 Proxy 的 Binder 对象。
onTransact():运行服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
transact():运行在客户端,当客户端发起远程请求的同时将当前线程挂起。之后调用服务端的 onTransact() 直到远程请求返回,当前线程才继续执行。

当有多个业务模块都需要 AIDL 来进行 IPC,此时需要为每个模块创建特定的 aidl 文件,那么相应的 Service 就会很多。必然会现系统资源耗费严重、应用过度重量级的问题。解决办法是建立 Binder 连接池,即将每个业务模块的 Binder 请求统一转发到一个远 Service 中去执行,从而避免重复创建 Service。
工作原理:每个业务模块创建自己的 AIDL 接口并实现此接口,然后向服务端提供自己的唯一标识和其对应的 Binder 对象。服务端只需要一个 Service,服务器提供一个 queryBinder 接口,它会根据业务模块的特征来返回相应的 Binder 对像,不同的业务模块拿到所需的 Binder 对象后就可进行远程方法的调用了。

2.0.0.6 Android 中有哪些基于 Binder 的 IPC 方式?简单对比下?

2.0.0.7 为何说 Binder 相比传统的 Socket 性能更高效?为何说 Binder 相比传统 IPC 安全性更高?

为何说 Binder 相比传统的 Socket 性能更高效?

跨进程通讯中,只有 socket 支持 Client-Server 的通信方式,但是 socket 作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。
消息队列和管道采用存储 - 转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。
共享内存虽然无需拷贝,但控制复杂,难以使用。

为何说 Binder 相比传统 IPC 安全性更高?

首先传统 IPC 的接收方无法获得对方进程可靠的 UID 和 PID(用户 ID 进程 ID),从而无法鉴别对方身份。Android 为每个安装好的应用程序分配了自己的 UID,故进程的 UID 是鉴别进程身份的重要标志。使用传统 IPC 只能由用户在数据包里填入 UID 和 PID,但这样不可靠,容易被恶意程序利用。可靠的身份标记只有由 IPC 机制本身在内核中添加。其次传统 IPC 访问接入点是开放的,无法建立私有通道。比如命名管道的名称,systemV 的键值,socket 的 ip 地址或文件名都是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。
基于以上原因,Android 需要建立一套新的 IPC 机制来满足系统对通信方式,传输性能和安全性的要求,这就是 Binder。
Binder 基于 Client-Server 通信模式,传输过程只需一次拷贝,为发送发添加 UID/PID 身份,既支持实名 Binder 也支持匿名 Binder,安全性高。技术博客大总结

2.0.0.8 Service Manager 是如何成为一个守护进程的?Server 和 Client 是如何获得 Service Manager 接口的?

Service Manager 是如何成为一个守护进程的?

Service Manager,它是整个 Binder 机制的守护进程,用来管理开发者创建的各种 Server,并且向 Client 提供查询 Server 远程接口的功能技术博客大总结

既然 Service Manager 组件是用来管理 Server 并且向 Client 提供查询 Server 远程接口的功能,那么,Service Manager 就必然要和 Server 以及 Client 进行通信了。我们知道,Service Manger、Client 和 Server 三者分别是运行在独立的进程当中,这样它们之间的通信也属于进程间通信了,而且也是采用 Binder 机制进行进程间通信,因此,Service Manager 在充当 Binder 机制的守护进程的角色的同时,也在充当 Server 的角色,然而,它是一种特殊的 Server,下面我们将会看到它的特殊之处

Service Manager 在用户空间的源代码位于 frameworks/base/cmds/servicemanager 目录下,主要是由 binder.h、binder.c 和 service_manager.c 三个文件组成。Service Manager 的入口位于 service_manager.c 文件中的 main 函数:
int main(int argc, char **argv){
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
LOGE(“cannot become context manager (%s)\n”, strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}

main 函数主要有三个功能:一是打开 Binder 设备文件;二是告诉 Binder 驱动程序自己是 Binder 上下文管理者,即我们前面所说的守护进程;三是进入一个无穷循环,充当 Server 的角色,等待 Client 的请求

Server 和 Client 是如何获得 Service Manager 接口的?

ServiceManager 作为守护进程,Service Manager 的职责当然就是为 Server 和 Client 服务了。那么,Server 和 Client 如何获得 Service Manager 接口,进而享受它提供的服务呢?
Service Manager 在 Binder 机制中既充当守护进程的角色,同时它也充当着 Server 角色,然而它又与一般的 Server 不一样。对于普通的 Server 来说,Client 如果想要获得 Server 的远程接口,那么必须通过 Service Manager 远程接口提供的 getService 接口来获得,这本身就是一个使用 Binder 机制来进行进程间通信的过程。而对于 Service Manager 这个 Server 来说,Client 如果想要获得 Service Manager 远程接口,却不必通过进程间通信机制来获得,因为 Service Manager 远程接口是一个特殊的 Binder 引用,它的引用句柄一定是 0。
经过一系列的调用 …
回到 defaultServiceManager 函数中,最终结果为:gDefaultServiceManager = new BpServiceManager(new BpBinder(0));
这样,Service Manager 远程接口就创建完成了,它本质上是一个 BpServiceManager,包含了一个句柄值为 0 的 Binder 引用。技术博客大总结

在 Android 系统的 Binder 机制中,Server 和 Client 拿到这个 Service Manager 远程接口之后怎么用呢?

对 Server 来说,就是调用 IServiceManager::addService 这个接口来和 Binder 驱动程序交互了,即调用 BpServiceManager::addService。而 BpServiceManager::addService 又会调用通过其基类 BpRefBase 的成员函数 remote 获得原先创建的 BpBinder 实例,接着调用 BpBinder::transact 成员函数。在 BpBinder::transact 函数中,又会调用 IPCThreadState::transact 成员函数,这里就是最终与 Binder 驱动程序交互的地方了。回忆一下前面的类图,IPCThreadState 有一个 PorcessState 类型的成中变量 mProcess,而 mProcess 有一个成员变量 mDriverFD,它是设备文件 /dev/binder 的打开文件描述符,因此,IPCThreadState 就相当于间接在拥有了设备文件 /dev/binder 的打开文件描述符,于是,便可以与 Binder 驱动程序交互了。
对 Client 来说,就是调用 IServiceManager::getService 这个接口来和 Binder 驱动程序交互了。具体过程上述 Server 使用 Service Manager 的方法是差不多的。

关于其他内容介绍
01. 关于博客汇总链接

1. 技术博客汇总

2. 开源项目汇总

3. 生活博客汇总

4. 喜马拉雅音频汇总

5. 其他汇总

02. 关于我的博客

我的个人站点:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/…

简书:http://www.jianshu.com/u/b7b2…

csdn:http://my.csdn.net/m0_37700275

喜马拉雅听书:http://www.ximalaya.com/zhubo…

开源中国:https://my.oschina.net/zbj161…

泡在网上的日子:http://www.jcodecraeer.com/me…

邮箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a… 239.headeruserinfo.3.dT4bcV
segmentfault 头条:https://segmentfault.com/u/xi…

掘金:https://juejin.im/user/593943…

正文完
 0