Android系统启动篇
4,《Android SystemServer进程启动流程》
Android系统开发准备篇
3,《Android Framework代码IDE加载和调试》
Android系统开发实践篇
4,《android单独编译framework模块并push》
Android系统开发核心知识储备篇
1,《Android编译系统-envsetup和lunch代码篇》
6,《Android中Activity、View和Window关系详解》
11,《android中AMS进程通知Zygote进程fork新进程的通信方式》
Android核心功能详解篇
2,《Android 手势导航(从下往上滑动进入多任务页面)》
3,《android手势分析(应用界面左往右边滑动退出应用)》
———————————————————————————————————————————
目录
一,背景介绍
1.1 简介
Binder机制是Android系统提供的一种跨进程通信机制,它使用代理对象、共享内存和序列化等技术,实现了进程间通信和远程调用的功能。它允许在不同进程之间进行数据传输和方法调用,实现了进程间的解耦。在Android系统中,Binder被广泛应用于各种组件之间的通信,例如Activity与Service、Service与Service、应用与系统服务等。
1.2 核心概念
- 驱动层:Binder作为一个设备驱动存在于Linux内核中。当应用程序与Binder对象进行通信时,实际上是在与Binder驱动进行交互。Binder驱动负责管理Binder对象、传输数据以及进程间的上下文切换等工作。设备文件节点通常是
/dev/binder。
-
用户空间库:Binder提供了一套C++库和Java库,以方便用户空间的应用程序使用。这些库包括序列化/反序列化数据的工具、用于管理Binder对象的引用计数的工具以及在进程间调用方法时的代理(proxy)和存根对象(stub)等。在Java层,通常使用AIDL(Android Interface Definition Language)来定义跨进程接口。
-
代理与存根:Binder使用了代理(Proxy)和存根(Stub)对象来实现进程间的方法调用。当一个进程要调用另一个进程中的方法时,它会通过代理对象将方法调用转换为一个Binder事务。然后,这个事务会被发送到接收进程,由存根对象解析并执行对应的方法。最后,存根对象将结果返回给代理对象,完成整个跨进程调用。
-
序列化与反序列化:在进程间传输数据时,需要将数据序列化为字节流,以便在不同进程之间进行传输。在接收进程中,数据会被反序列化为原始格式。Binder提供了一套序列化和反序列化的工具(如Parcel类),用于在进程间传输数据。
-
引用计数与死亡通知:Binder机制通过引用计数来管理Binder对象的生命周期。当一个进程获得了另一个进程的Binder对象引用时,引用计数会增加。当引用计数减少到零时,Binder对象会被销毁。另外,Binder还支持死亡通知机制,允许一个进程监听另一个进程的Binder对象死亡事件。
二,Binder原理
2.1 进程空间
进程空间划分:用户空间(User Space) ——内核空间(Kernel Space)
1,每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。
2,对于用户空间,不同进程之间是不能共享的,而内核空间却是可共享的
3,Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的
4,Client端与Server端进程往往采用ioctl等方法与内核空间的驱动进行交互。
2.2 Binder一次拷贝
Android 系统中用户进程间是如何通过内核模块(Binder 驱动)来实现通信的呢?当然不是之前的将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程。而是通过内存映射:
1,内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间,映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间
2,反之内核空间对这段区域的修改也能直接反应到用户空间。内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动
Binder IPC 正是基于内存映射(mmap)来实现的,但是 mmap通常是用在有物理介质的文件系统上,进程中的用户区域是不能直接和物理设备打交道的。如果想要把磁盘上的数据读取到进程的用户区域,需要两次拷贝(磁盘–>内核空间–>用户空间),通常在这种场景下 mmap 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。
副本直接copy进内核态映射给B用,那么什么是内存映射,
-
底层原理其实就是虚拟内存
-
简单来说:不同的虚拟内存指向相同的物理内存,从而实现共享内存和共享文件
一次完整的Binder IPC 通信过程通常是这样,
1,Binder 驱动在内核空间创建一个数据接收缓存区;
2,然后在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
3,发送方进程通过系统调用 copy_from_user() 将数据拷贝到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
2.3 Binder使用
Binder通信的四个角色:
1,Client进程:使用服务的进程。
2,Server进程:提供服务的进程。
3,ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
4,Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
Binder通信采用c/s架构,包含Client、Server、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。
Server先向ServiceManager注册一个服务,其实也就是一个字符串。然后Client从ServiceManager获取服务,关键字就是注册的字符串。这样Client和Server就可以通信。真正的数据流是走了底层的Linux内核空间的binder驱动,但这个是被封装的,所以不用关心真正的binder驱动,只管在Client和Server之间调用函数收发数据就行。就是服务端的onTransact函数和客户端remote()->transact(TEST, data, &reply),发送的数据存在data中,返回的数据存在reply中。Client和Server都可以收发数据。
上图中Client、Server和ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。
1,注册服务(addService):Server进程要先注册Service到ServiceManager。该过程:Server是客户端,ServiceManager是服务端。
2,获取服务(getService):Client进程使用某个Service前,须先向ServiceManager中获取相应的Service。该过程:Client是客户端,ServiceManager是服务端。
3,使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:Client是客户端,Server是服务端。
上图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信(Interprocess Communication)方式。
其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。
Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现Client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。
Client、Server、ServiceManager、Binder 驱动 就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder 驱动)之前的关系。
2.4 Binder通信过程
1、首先,一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
2、Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。
3、Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。
三,实战
程序跨进程调用系统服务的简单示例,实现浮动窗口部分代码:
//获取WindowManager服务引用
WindowManager wm = (WindowManager) getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view = LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
1,注册服务(addService): 在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理)。这一步是系统自动完成的。
2,获取服务(getService): 客户端想要得到具体的Service直接向ServiceManager要即可。客户端首先向ServiceManager查询得到具体的Service引用,通常是Service引用的代理对象,对数据进行一些处理操作。即第2行代码中,得到的wm是WindowManager对象的引用。
3,使用服务: 通过这个引用向具体的服务端发送请求,服务端执行完成后就返回。即第6行调用WindowManager的addView函数,将触发远程调用,调用的是运行在systemServer进程中的WindowManager的addView函数。
1,Client通过获得一个Server的代理接口,对Server进行调用。
2,代理接口中定义的方法与Server中定义的方法是一一对应的。
3,Client调用某个代理接口中的方法时,代理接口的方法会将Client传递的参数打
包成Parcel对象。
4,代理接口将Parcel发送给内核中的Binder Driver。
5,Server会读取Binder Driver中的请求数据,如果是发送给自己的,解包Parcel
对象,处理并将结果返回。
6,整个的调用过程是一个同步过程,在Server处理的时候,Client会Block住。因
此Client调用过程不应在主线程。
————————————————
参考:Yawn__
原文链接:https://blog.csdn.net/ly0724ok/article/details/117566381/