Android Binder通信原理详解

Android通信系列目录

  1. Android Binder通信原理详解
  2. Android 内存映射mmap浅谈

博客创建时间:2020.11.06
博客更新时间:2020.11.09


前言

Binder机制是​ Android系统中跨进程通讯(IPC)的一种方式,Android中ContentProvider、Intent、aidl都是基于Binder。

IPC除了Binder,还有共享内存、Socket、消息队列等其他方式。相比其他的跨进程通信方式Binder有其自身特有的优点:

  1. Binder使用mmap机制,只拷贝了一次数据,性能上仅次于共享内存
  2. 采用Client/Server架构,实现面向对象的调用方式,调用如同调用Java对象,使用简单
  3. 为每个App分配了UID/PID来鉴别身份标识,通信时检测UID/PID进行有效性检测,提高了安全性

阅读该篇博文前建议先阅读《Android 内存映射mmap浅谈》,这样更利于帮助理解Binder通信。


Binder机制

用户空间访问内核空间的唯一方式就是系统调用;通过这个统一入口接口,所有的资源访问都是在内核的控制下执行,以免导致对用户程序对系统资源的越权访问,从而保障了系统的安全和稳定。

Binder通信采用C/S架构,包含Client、 Server、 ServiceManager 以及 Binder 驱动。在 framework 层进行了封装,通过 JNI 技术调用 Native(C/C++)层的 Binder 架构,在 Native 层以 ioctl 的方式与 Binder 驱动通讯。

在这里插入图片描述

Client进程
使用服务的进程。

Server进程
提供服务的进程。

ServiceManager进程
Android OS的整个服务的管理程序,在binder通信中ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。

任何service在被使用之前,均要向SM(Service Manager)注册。当客户端需要访问某个service时,应该首先向SM查询是否存在该服务。如果SM存在这个service,那么会将该service的handle返回给client,handle是每个service的唯一标识符。

Binder
一个进程的Binder线程数默认最大是16,超过的请求会被阻塞等待空闲的Binder线程。所以,在进程间通信时处理并发问题时,如使用ContentProvider时,它的CRUD(创建、检索、更新和删除)方法只能同时有16个线程同时工作

Binder驱动
驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。

一个Binder驱动中有很多个Binder对象,通过InterfaceToken区分他们


Binder通信流程
在这里插入图片描述


Binder通信案例

了解进程间通信的人都知道在Android使用的是Binder进行进程间通信,它的核心是mmap 内存映射方式进行的跨进程通信。

Binder机制在 Android中的实现主要依靠 Binder类以 MyClient进程调用 MyServer进程 的加法函数,a+b举例来分析其过程

1. 获取服务
MyClient进程通过Binder驱动 向 ServiceManager进程 查询相应的Service是否已注册,如未注册则注册服务。最终MyClient进程获得BinderProxy对象。

此时 MyClient进程MyServer进程建立了连接。

  1. MyClient进程通过binderService()向Binder驱动发起获取Service的请求,并传递要获取的服务名。
  2. Binder驱动将该请求转发给ServiceManager进程
  3. ServiceManager将要查找的 MyServer进程Binder对象的引用信息返回给Binder驱动,该引用对象实质是指BinderProxy对象
  4. Binder驱动将MyServer进程Binder对象的引用信息返回给MyClient进程,通过onServiceConnected()获得Binder对象的代理对象BinderProxy对象。

2. 注册服务
MyServer进程进程通过Binder驱动 向 Service Manager进程注册服务,通过调用该Service的onBinder返回一个Binder对象到Binder驱动中

  1. Binder 对象是 MyServer进程 在 Binder 驱动中的存在形式,存在于内核空间中
  2. 该对象保存 Server 和 ServiceManager 的信息
  3. Binder 驱动通过 内核空间的Binder 实体 找到用户空间的Server对象

3. 使用服务
MyClient进程通过BinderProxy对象操作Binder驱动内的Binder对象,进而操作MyServer进程

  1. MyClient进程将参数(整数a和b)发送到 MyServer进程
  2. MyServer进程 根据MyClient进程要求调用 目标方法(即加法函数)
  3. MyServer进程将目标方法的结果(即加法后的结果)返回给MyClient进程

在这里插入图片描述
过程分析

  1. 从表面上来看,是 MyClient进程获得一个MyServer进程 的代理接口,对MyServer进程进行直接调用
  2. 实际上,代理接口中定义的方法与MyServer进程 中定义的方法是一一对应的
  3. MyClient进程调用某个代理接口中的方法时,代理接口的方法会将MyClient进程传递的参数打包成为Parcel对象
  4. 代理接口将该Parcel发送给内核中的binder driver.
  5. MyServer进程 会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回
  6. 整个的调用过程是一个同步过程,在MyServer进程 处理的时候,MyClient进程的当前线程block阻塞

跨进程通信(IPC)

在说Binder通信之前先说说跨进程通信IPC。

我们知道为了保证进程的安全性与独立性,一个进程不能直接操作或者访问另一个进程,即Android的进程是相互独立、隔离的。

如果进程间需要进行数据交换和通信就需要用到跨进程通信(IPC)。跨进程通信有两种方式,传统方式mmap方式

1. 传统的跨进程通信

  1. 发送进程通过系统调用,将需要发送的数据从用户空间copy_from_user拷贝到Linux进程的内核空间中(Page Cache)

  2. 内核服务程序唤醒接收进程的接收线程,通过系统调用将数据从内核空间中copy_to_user发送到用户空间中

  3. 接收进程获得用户空间中的数据,完成了数据的跨进程通信
    在这里插入图片描述

缺点:
5. 效率低下,需要2次数据拷贝, 用户空间>>内核空间>>用户空间
6. 接收器的数据缓存需要接收方来提供,但是接收方却不知道到底要多大的缓存才能满足需求
尽量开辟大的空间,这样容易浪费空间;先调用API接收消息头获得信息体大小然后确定缓存大小,该方法浪费时间


2. mmap跨进程通信
以Android中Binder IPC跨进程通信为例
7. 发送进程对/dev/binder设备调用mmap()
8. 内核中的的binder_mmap函数进行对应的处理,申请到一块物理地址,即共享缓存区
9. 根据需要映射的接收进程信息,实现接收进程和发送进程的虚拟内存空间同时映射到同一个共享缓存区
10. 发送进程将数据从用户空间发送到自生的虚拟内存区域,其实质是拷贝到了共享缓冲区
11. 因为接收进程的虚拟空间同样指向共享缓存区,当共享缓存区数据改变时,相当于接收进程已经获得了新数据
在这里插入图片描述

优点:
12. 传输效率高,拷贝次数1次,用户空间可以直接通过共享对象直接交互
13. 直接跨过了内核空间,在物理地址分配了接收缓存区

注意进程中的空间分为用户空间内核空间用户空间中数据不可共享,内核空间中的数据可以共享。


补充说明

说明1
MyClient进程MyServer进程 & Service Manager 进程之间的交互 都必须通过Binder驱动(使用 open 和 ioctl文件操作函数),而非直接交互
原因:

  1. MyClient进程MyServer进程 & Service Manager进程属于进程空间的用户空间,不可进行进程间交互
  2. Binder驱动 属于 进程空间的 内核空间,可进行进程间 & 进程内交互
    在这里插入图片描述

说明2
Binder驱动 & Service Manager进程 属于 Android基础架构(即系统已经实现好了);而Client 进程 和 Server 进程 属于Android应用层(需要开发者自己实现)
所以,在进行跨进程通信时,开发者只需自定义Client & Server 进程 并 显式使用上述3个步骤,最终借助 Android的基本架构功能就可完成进程间通信
在这里插入图片描述


思考总结

本篇博文主要讲解Binder原理及通信流程,弄懂如下几个问题

  1. Binder是什么?为什么要使用它,比如它有何优点?
  2. 它是如何实现跨进程通信的? 以一次跨进程函数为例,调用过程是怎样的?

相关链接

  1. Android Binder通信原理详解
  2. Android 内存映射mmap浅谈

博客书写不易,您的点赞收藏是我前进的动力,千万别忘记点赞、 收藏 ^ _ ^ !

猜你喜欢

转载自blog.csdn.net/luo_boke/article/details/109307263
今日推荐