1. 先抛出问题:Binder 是什么?
做过Android开发的同学估计都知道,Android系统层的IPC机制使用了一个新的机制,也就是Binder机制。并且在Android Framework层面非常依赖该机制,比如四大组件中的跨进程通信都会使用到 Binder 的机制。
那么,Binder 究竟是什么呢?
我也从互联网上、书上等等找了很多关于Binder的文章以及说明,但是发现大部分介绍Binde机制的都是在翻来覆去重复着那几个关键概念,如Binder驱动、一次拷贝、内容映射,然后就是贴一堆AOSP中的相关源码,画一大堆系统函数的调用次序等等。而鲜有哪些文章能够真的站在一个较为高一些的视角从整体的角度去讲述Binder 这个框架到底是什么?是为了解决什么问题?
知其然,更要知其所以然,否则将会深陷在一个个模糊不清的概念中,和一堆堆琐碎混乱的代码逻辑中,而无法使自己的思维脱身。
接下来,本文将会从一个较为宏观的角度来介绍Binder 框架,使得读者能够对Binder框架有一个整体的认知,所以不会涉及详细的源码分析,毕竟,那不是本文的目的。
在这里,我们先抛出问题:Binder 到底是什么?
2. OpenBinder 项目是怎么回事?和Binder 是什么关系?
2.1 为什么Android会选择Binder?
从哲学的角度来讲,当问到一个事物“是什么?”的时候,那么将会不可避免地要接着问“它来自何方?”以及“它将去向何处?”。
那么,我们先从Binder 的身世来入手,即Binder 来自何方?
Android系统中使用的Binder 机制,实际上是起源于一个叫BeOS
操作系统中的一个功能,后来BeOS 被Palm收购了。这个Palm 就是鼎鼎大名的 Andy Rubin 创建的。后来Palm 又被其他公司收购了,之后Andy Rubin就去创建了 Android
。同时他还带走了原本在 Palm 公司的一些人,其中就有这个Binder
框架的开发者 Dainne Hackborn。
在开发Android系统的时候,面对IPC的需求,团队肯定也调研过linux 上目前就存在的几种传统IPC机制,比如管道、Socket、匿名共享内存等等,但是他们或多或少的都不太满足Android系统的需求。Android作为一个移动终端来说,会对IPC机制有以下方面的需求:
- 安全性:要对通信的进程有验证;
- 性能高:这个毋庸置疑,移动设备的性能通常比不上PC,而且电池电量也小,需要性能高、耗电少;
- 使用简单:要能快速地把系统开发出来。
所以,在Android系统选中了Binder 作为系统的IPC方案这个问题上,其实原因很简单,那就是他们已经有相关的经验了。这几乎是一个顺理成章的时候。
后来,Dainne 把Binder 的框架开源了,就是OpenBinder
项目。
2.2 OpenBinder 是什么?
OpenBinder 是使用C++开发的一个开源项目,项目地址在:http://www.angryredplanet.com/~hackbod/openbinder/docs/html/index.html
Android系统中内置的Binder相关的部分,和OpenBinder 没有什么大的不同。所以我们先看一下OpenBinder 是一个什么样的项目?
在官方说明中有一句话可以概括下OpenBinder 是什么:
The Binder defines and implements a distributed component architecture similar in broad strokes to COM on Windows and CORBA on Unix.
Binder定义并且实现了一个类似于Windows 系统的COM框架和Unix系统的CORBA框架的分布式组件框架。
来源参考:http://www.angryredplanet.com/~hackbod/openbinder/docs/html/index.html
这个描述可以说是非常精简,同时也给出了OpenBinder 的官方定义。
它是一个分布式组件框架,可是这又是什么呢?在它的描述中提到了COM 和 CORBA两个框架。这两个本质上是一样的设计,这里我们只介绍CORBA了。
CORBA的全称是“(Common Object Request Broker Architecture)”,它的诞生非常早,提出了非常先进的理念:可以实现执行本地其他设备或者网络上其他设备的程序,而且就像在本地执行一样。这个理念发展到现在(2020年),其实就是现在非常成熟的RPC理念。
不过CORBA设计有些过于复杂,并没有发展太好,我相信在看文本的读者中没几个听说过这个东西的。它被戏称为 “一群象牙塔里面的人设计的东西“。不过虽说CORBA技术基本已经被淘汰了,但是承袭了它理念的“RPC”可以说是已经应用非常广泛,是互联网技术的一个重要基石了。
然后我们再回到OpenBinder,既然它声称自己是类似CORBA
的,那么其实就很好理解OpenBinder 的定位了。按照现代的语言来说,它本质上就是一个 RPC
的框架。关于RPC相关的知识,本文不再赘述,请参考 远程过程调用-维基百科
和一个普通的RPC不太一样的是,OpenBinder 的设计目标就是在针对系统的,它不会被应用层直接使用,而是需要被内置到系统里面。
2.3 Binder 在Android上的应用
在熟悉了Binder 的“身世”之后,我们对它的理解其实就比较明晰了,Binder 原本就是一个RPC的框架,那么在Android系统的,作为一种IPC
的实现,当然是完全能够胜任的,甚至还有点杀鸡用宰牛刀的感觉。理论上Binder机制是很强大的,比如能够实现多台Android设备上的远程调用。
所以Android上的四大组件通信、服务间的通信,几乎全都是构建在Binder 通信的基石之上了。
3. Binder IPC是怎么做的?
Binder 框架是一个完整的RPC框架,本地的IPC支持只是它其中的一个功能点。在Android系统中就是用到了它的本地进程通信IPC的能力。那么,接下来我们看下Binder IPC 是怎么实现的。
首先,Android是基于Linux 的,而Linux的进程在用户空间是天然隔离的,只有内核空间是共享的,所以在Linux 上要实现一种IPC机制必然是要借助内核的。
我们先想想,在Linux 系统上,要把进程A的数据,传递到进程B中,该怎么操作呢?
graph TD A[进程A] -->|复制数据| B(内核空间) B -->|复制数据| C[进程B]
这里能看到,会有两次数据的复制,那么有没有什么办法能减少数据复制呢?
这里就要提到一个关键的技术了,那就是linux 系统提供的 mmap()
系统调用(详细可以参考http://man7.org/linux/man-pages/man2/mmap.2.html)。
mmap 函数,简单来说,就是能够把一个文件的内容,映射到内存里面,使得进程之间对这个内存空间的操作能够同步到那个文件上,同时文件里面的改动也能同步到内存里面。需要注意的是,这里说的文件
,指的是linux上广义的文件。在linux上,一切皆文件。
有了mmap 机制的支持,我们就可以这个做):
graph TD BU[进程B用户空间] -->|内存映射| K A[进程A] -->|复制数据| K(内核/Binder驱动)
这其中”内存映射” 这个过程,是在B进程启动的时候,就已经做过映射的。这里映射的,是一个驱动 /dev/binder
,这个驱动也是一个 ”文件“。每一个进程启动后,都会在自己的进程内部指定一块内存,用来接收来自启动程序的数据。这一块内存区域会和内核空间的文件 /dev/binder
进行映射。
当有IPC要发生的时候,进程A之间把数据复制到内核中,也就是写到 /dev/binder
里面对应的位置,正好这一段位置就是进程B的用户空间。
我们可以看到,binder 驱动在这个机制中担任了核心的位置,它是一个虚拟的驱动,并没有真实的设备,在IPC的时候把它当作一个文件来和用户空间的内存进行一个映射。也就是这个内存映射的存在,使得数据的复制次数降低到了1,也就是所谓的“一次拷贝”。
然后,我们来回答最初的那个问题:Binder 是什么?
答:Binder 是来自OpenBinder框架的一个用于本地IPC的技术。它使用了linux 的mmap机制把进程的用户空间的内存映射到一个叫做 /dev/binder
驱动设备上,使得进程间传递数据只需要复制就能完成。