4

Binder纯理论分析 | Rouse

 3 years ago
source link: http://www.rousetime.com/2021/01/21/Binder%E7%BA%AF%E7%90%86%E8%AE%BA%E5%88%86%E6%9E%90/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Binder纯理论分析

2021.01.21

Rouse

android

 热度 11℃

接下来的一段时间,我们会来深入认识一下Android中的Binder机制。

今天的主要是来对Binder做一个较全面的介绍,为之后的深入分析做一个预热准备。

Linux IPC

首先BinderAndroid中的一种独有的跨进程通信方式,简称IPC。它是专门为Android平台设计的。

那为什么要设计出Binder这个烦人的东西呢?我们都知道Android是基于Linux系统进行演变过来的,所以理应也能直接使用LinuxIPC通信方式。

所以在理解Binder的设计初衷之前,我们先来了解一下Linux系统中现有的IPC通信方式。

Linux现有的IPC通信方式有6种:

  1. socket

英文为pipe,在Linux中它的本质是一个文件系统,通过一个进程以写的方式打开文件,另一个进程以读的方式进行打开文件,通过这样读写的方式,实现了进程间的通信。

只不过该文件是位于Linux内存中,所以操作管道就是以文件的方式操作Linux内存缓存区。

由于管道是通过读写文件的方式进行运作的,所以它需要进行两次数据的拷贝;分别是copy_form_user从写进程拷贝到文件内存缓存区,再通过copy_to_user从文件缓存区拷贝到读进程中。

同时管道还有大小限制,默认为4k,一旦写入端超过大小限制,管道将会阻塞。

主要作用于进程间的资源互斥访问,通过PV两种操作等待与发送信号。

P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

所以信号量主要是用来解决多个进程对同一资源的竞争问题,类似于多线程的同步锁。

Linux中定义的一种软中断,有64种,分为可靠信号与不可靠信号,多用于消息传递与通知,不适合传递信息。

共享内存顾名思义,允许不同的进程访问同一块内存地址空间,它也需要进行两次数据拷贝操作,分别是将数据拷贝到共享内存中,又从共享内存中间将数据拷贝出来。

但需要注意的是,共享内存是不提供同步机制。

意思就是说,在其中一个进程进行写操作时,并不能放在另一进程进行读操作。

为了解决这个问题,共享内存一般都与前面说的信号量一起使用。

消息队列通过一个进程向另一个进程发生消息块的方式进行通信,它与管道非常类似,都需要发送与接收,数据拷贝两次。

  1. 消息队列可以防止同步与阻塞问题。
  2. 消息队列的接收方可以进行选择性接收。
  3. 发送的消息块有最大限制

socket

Linux中的socket是基于C/S架构的,传输效率低,多用于跨网络与跨设备的通信。

Android底层使用socket来进行initzygote等进程间的通信。

最后简单的来看一张图来了解在Linux中不同进程中的通信过程。

Linux

所以通过上面的分析,Linux现有的几种IPC通信方式都不是很适合Android间的进程通信。

例如管道、共享内存与消息队列都需要拷贝两次数据,同时有的还会存在阻塞与同步问题;另外的信号、信号量与socket由于使用场景的原因,都不适合用于Android中快速的进程间的数据通信。

Binder

那么Binder通信方式是怎么样的呢?

Binder本身是基于C/S架构的,层次分明,架构稳定,同时Binder内部只需使用一次数据拷贝操作,就能达到进程间数据的通信;另外Binder还支持鉴别用户进程的Uid,为Android提供身份的验证。

我们先来通过一张图来简单看下基于Binder的进程通信过程

Binder数据通信流程是,将数据从client端拷贝到内核空间,在内核空间中会提前通过mmap方式建立与server端的内存地址映射,通过内存地址映射server端可以直接访问内核空间中的数据,从而避免将数据拷贝到server端,提高进程间的通信速度。

在整个通信的过程中主要做的事情是:

  1. 通过/dev/binder打开binder驱动
  2. 通过mmap建立内存地址映射
  3. 通过ioctlbinder驱动交互,进行数据传输
  4. client使用BpBindertransact方法进行事务请求
  5. server使用BBinderonTransact方法来接收相应的事务

由于Android中主要使用Binder来进行service的注册与获取,所以为了更好的管理service的注册,使用了ServiceManager来进行统一管理service的注册。

service的注册过程中ServiceManager就相当于server端,内部开启loop循环,不断接收消息将注册的service保存到注册表svclist中。

最终关于Binder的整个大致流程如下图所示

其中clientserver位于应用层,ServiceManager位于用户空间,binder驱动位于内核空间。

对于开发者来说,用户空间与内核空间是透明的,我们只需关注应用层clientserver的实现,就可以方便的使用Binder的通信机制。

以上是对Binder的一个理论分析,接下来的一段时间,我将结合源码来分析service的注册过程,从而探索Binder的整个工作流程,感兴趣的读者可以关注一下。

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用android-startup来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。 与此同时android-startup支持同步与异步等待,并通过有向无环图拓扑排序的方式来保证内部依赖组件的初始化顺序。

AwesomeGithub: 基于Github客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于Jetpack&DataBindingMVVM;项目中使用了ArouterRetrofitCoroutineGlideDaggerHilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 算法进阶,由浅入深,欢迎加入一起共勉。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK