Binder纯理论分析

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

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

Linux IPC

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

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

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

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

  1. 管道

  2. 信号量

  3. 信号

  4. 共享内存

  5. 消息队列

  6. 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 现有的几种 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. 通过
    ioctl
    binder 驱动交互,进行数据传输
  4. client
    BpBinder
    transact
    
  5. server
    BBinder
    onTransact
    

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

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

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

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

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

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

福 利

为了感谢大家大支持,我特意搞了一个粉丝抽奖福利,只需在公众号后台回复【Android补给站,必出精品】关键字,即可获取二维码进行抽奖,小憩提前预祝大家中奖。

扫码二维码

获取更多精彩

壹伴编辑器