欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 培训 > Android中的Binder

Android中的Binder

2025/3/12 22:11:19 来源:https://blog.csdn.net/qq_44016573/article/details/140990946  浏览:    关键词:Android中的Binder

binder是Android平台的一种跨进程通信(IPC)机制,从应用层角度来说,binder是客户端和服务端进行通信的媒介。

ipc原理

ipc通信指的是两个进程之间交换数据,如图中的client进程和server进程。

Android为每个进程提供了虚拟内存空间,而每个Android进程只能运行在自己进程所拥有的虚拟内存空间。

内存空间又分为用户空间和内核空间,前者的数据不能进程间共享,但后者可以。图中的Client进程和Server进程就是利用了进程间可以共享各自内核空间的数据,来完成底层通信的工作。

Android的C/S通信机制

C/S通信指的就是Client和Server两个进程的通信,但实际通信时除了包含这两个进程,还有一个Service Manager,它用于管理各种服务。

这些服务通常是Android系统的核心功能模块,例如传感器管理、电源管理、WIFI管理、闹钟服务等等,与Android四大组件中的服务不同。

当一个Server(服务端)想要提供一种服务,首先需要在Service Manager注册该服务;

而当Client(客户端)想要使用Server中的服务时,不能直接访问,而是要从Service Manager获取该服务,才能使用Server所提供的服务,来与Server进行通信。

Binder通信模型

在引入binder机制后,客户端、服务端和Service Manager之间不能通过api直接互相访问,而是与内核空间的binder驱动通过ioctl方式来完成进程间的数据交换。

关键概念

  • Binder实体对象:Binder服务的提供者,类型是BBinder,位于服务端
  • Binder引用对象:Binder实体对象在客户端进程的代表,类型是BpBinder,位于客户端
  • IBinder对象:Binder实体对象和引用对象的统称,也是他们的父类
  • Binder代理对象:又称接口对象,为客户端的上层应用提供接口服务,类型是IInterface

Binder引用对象和代理对象都是服务端进程中的,把它们分离的好处是一个代理对象可以有多个引用对象,方便上层应用使用。

通信过程

注册服务

  1. server进程向binder驱动申请创建服务的binder实体 
  2. binder驱动为这个服务创建位于内核的binder实体和binder引用
  3. 创建完成后,服务端通过binder驱动将binder引用发送给service manager
  4. service manager收到数据后,取出被创建服务的名字和引用,填入一张查找表

通过以上步骤,server进程通过binder驱动完成了在service manager的服务注册。

在注册服务的过程中,server进程是客户端,而service manager是服务端。

获取服务

  1. Client进程利用handle值为0的引用找到service manager
  2. Client进程向service manager发送xxxservice的访问申请
  3. service manager从请求表中获取xxxservice的名字,在查找表中找到对应的条目,取出对应的binder引用
  4. service manager把xxxservice的binder引用传给Client进程

使用服务

在使用服务时,Client和Server进程都是发送方和接收方。 

这是因为Client在发送服务请求时,Server是接收方;当Server返回数据给Client时,Client变成了接收方。

不论发送方是谁,都会通过自身的Binder实体,把数据发送给接收方的Binder引用。binder驱动回处理发送请求,利用内核空间进程共享机制如下:

  1. 把发送方的数据存入写缓存(binder_write_read.write_buffer)(对于接收方这是读缓存)
  2. 接收方一直处于阻塞状态,当写缓存有数据,会读取数据执行命令操作
  3. 接收方执行操作后,会把结果返回,同样放在写缓存区(对于发送方这是读缓存)

Android中的Binder

 activity、service等组件都需要与ams(system_server)通信,这种跨进程的通信是由binder完成的。从不同角度分析binder如下:

  • 机制:binder是一种进程间通信机制
  • 驱动:binder是一个虚拟物理设备驱动
  • 应用层:binder是一个能发起通信的java类:在java中,如果想要进程通信,就要继承binder

为什么要使用多进程进行开发?

虚拟机分配给各个进程的运行内存是有限制的,lmk也会优先回收占用系统资源大的进程。

对于多进程开发的优势一般有以下几点:

  • 突破进程内存限制,为占用内存大的单独开辟一个进程
  • 功能稳定性:如为通信线程保持长连接的稳定性
  • 防止内存泄漏:如为容易内存泄漏的webview单独开辟一个进程
  • 隔离风险:对于不稳定的进程放在独立进程,避免主进程崩溃

Binder有什么优势 

首先回顾linux进程间的通信机制:管道、信号量、共享内存、socket。

从性能出发,共享内存 > binder > 其他ipc。

但共享内存的缺点也十分明显。与线程之间共享同一块内存相同,共享内存的进程也很容易出现死锁、数据不同步等问题,操作不方便。同时,socket作为一款通用接口,开销过大。

最重要的一点是安全性。传统ipc模式普遍存在的问题是依赖上层协议和访问接入点是开放的。

以创建服务为例,系统需要知道创建人的身份,但在传统ipc机制中,这个身份的获取是从上层协议获取的,即app将id传给系统,而app传回的内容可以是不真实的。对比之下,服务在被创建时,binder就会为创建人分配唯一的uid(用户身份)。

第二点以服务器为例,如果ip是开放的,服务器很容易就会被攻击。同样的,对于传统ipc,如果接入点被知晓,所有人都可以访问。对比之下,binder同时支持实名和匿名。实名与传统ipc相同,是开放的;匿名指的是如果有人需要获取服务,需要先获取到binder内部的引用,才能进行访问。通常的,系统服务是实名的,个人服务是匿名的。直接在service manager中注册的服务是实名的。

Binder是如何做到一次拷贝的

在回答问题之前详细解释一下ipc和虚拟内存的概念。

进程间通信和线程间不同的原因是两者的内存机制不同。对于线程而言,它们的内存是共享的,但进程之间的内存是相互隔离的。

在ipc原理中我们看到进程内部分为用户空间和内核空间,出于安全两者之间是隔离的,app和系统分别处理用户空间和内核空间。

设想如果进程中没有对这两部份进行隔离,app就可以任意访问系统才能访问的数据,而正常来说这样的访问是需要权限的。但这不意味着两者之间完全隔离。系统为两者通信提供了api(copy_from_user & copy_to_user),使得两者间可以互相通信。

对于我们编程而言,需要系统分配虚拟内存,这是因为物理内存不一定是一整块内存,而这种整块内存恰恰是我们在编程中常常需要的。

虚拟内存是通过MMU内存管理单元来映射到物理内存的。在设计的时候,所有进程的内核空间都被映射到了同一块物理内存。这么做的好处就是实现了内存共享,一个进程可以很方便的去获取物理空间中其他进程的内核空间。

 以上就是传统的ipc通信方式。一个进程把自身用户空间的数据通过copy_from_user拷贝到内核空间,而另一个进程通过copy_to_user第二次拷贝从内核空间获取数据到自己的用户空间,这就拷贝了两次。

在binder机制下通信时,内核空间和数据接收方的用户空间映射了同一块物理内存。

简而言之,获取数据的进程的用户空间和内核空间都分配了一小块内存空间,指向同一块物理内存。而这就意味着当被获取数据的进程,从用户空间复制数据到内核空间后,如果内核空间把这个数据存储到这一小块内存空间,另一个进程就能够直接获取,不需要再做一次复制。

MMAP的原理

Linux将一个虚拟内存区域,与磁盘上的物理内存区域关联起来,以这种方式初始化这个虚拟内存区域的内容。这个过程称为内存映射(memory mapping)。

用户空间是不能直接访问磁盘上的内容的,如需访问要通过内核空间,这是肯定很慢的。 首先需要调用write方法从用户空间复制到内核空间,再把数据复制到磁盘,完成写入。

因此当我们使用mmap时关联了虚拟内存和物理内存,当我们在虚拟内存做操作时,物理内存就会直接被修改。

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词