七、Android中 IPC 机制(4)之 Binder --- 底层分析

转载:
https://blog.csdn.net/universus/article/details/6211589 & https://blog.csdn.net/carson_ho/article/details/73560642

摘要

    Binder 是 Android 系统进程间通信(IPC)的方式之一。虽然 Linux 系统有比如管道、消息队列、Socket 等 IPC 方式,但 Android 系统还是依赖 Binder 来实现进程间通信,说明 Binder 具有无可比拟的优势。了解 Binder 底层并将它与传统的 IPC 方式进行对比有助于我们深入领会 IPC 的实现原理和性能优化。而本文就结合上面两篇文章,简单的对 Binder 底层进行一下了解。

引言(为什么 Android 系统引入 Binder 进行进程间通信?)

    基于 Client-Server 的通信方式广泛应用于互联网、数据库访问以及嵌入式设备中。智能手机(特别是 Android 系统)为了向应用开发者提供丰富多样的功能,这种通信方式更是无处不在,比如媒体播放、音视频获取以及手机中的各种传感器都由不同的 Server 负责管理,开发者开发的应用程序只需要作为 Client 端与这些 Server 建立连接便可以很方便的使用这些服务。由于 Client 端和 Server 端分别在不同的进程中,所以这就会设计到进程间通信的问题,而 linux 系统中 Socket 支持这种跨进程通信方式,但是综合传输性能和安全性来说,Android 系统引入了 Binder 通信机制作为Android中 IPC 方式之一,下面看一下传统的 Socket 等跨进程通信方式和 Binder 之间的区别:

1. 传输性能

    在 Linux 系统中,一个进程空间包含用户空间和内核空间。在不同的进程间,用户空间的数据为各进程私有空间,其它进程无法访问;而内核空间为共享空间,即所有的进程共用同一个内核空间,它们都能访问该空间数据。

    在一个进程内,用户空间和内核空间可以通过下面两个系统函数进行数据交互:

1. copy_from_user()  // 将用户空间的数据拷贝到内核空间
2. copy_to_user()    // 将内核空间的数据拷贝到用户空间

图示:

而传统 IPC 方式如下图:

    而 Binder 机制只需要一次数据拷贝,所以传输效率更高。(具体下面分析)。

2. 安全性

    Android 系统作为一个开放式的平台,拥有众多的开发者,应用程序来源广泛,所以确保终端设备的安全是非常重要的。比如一个应用程序如果很轻易的就能够获取到系统其它应用(如联系人、短信)的信息,就会导致用户的隐私泄露等。而传统的 IPC 没有任何的安全措施,完全依赖上层协议来确保。而 Binder 机制使用 Android 系统为每个进程提供的 UID/PID 作为鉴别身份的标识,提高了进程间通信的安全性。

    基于以上原因,Android 系统建立了一套新的 IPC 机制来满足系统对通信方式、传输性能和安全性的要求,这就是 Binder。Binder 机制基于 Client- Server 通信方式,传输过程只要一次数据拷贝并通过进程的 UID/PID 进行身份鉴别,安全性高。

面向对象的 Binder

    Binder 使用 Client-Server 通信方式:一个进程作为 Server 提供诸如视频/音频解码、视频捕获、网络连接等服务,另一些进程可以作为 Client 向 Server 发起服务请求,建立连接以获取相应的服务。而要想实现 Client-Server 通信方式必须实现这两点:一是 Server 必须有确定的访问接入点或者地址作为 Client 请求的标识;二是 Server 端和 Client 端数据通信的协议。例如网络通信中 Server 的访问接入点就是 Server 主机的 IP 地址 + 端口号,c数据通信的协议为 TCP 协议。对 Server 而言,Binder 可以看成是 Server 提供的实现某个特定服务的访问接入点或地址,Client 可以通过这个“地址”向 Server 发送请求来使用该服务;对 Client 而言,Binder 可以看成是通向 Server 的管道入口,要想和某个 Server 通信首先必须建立这个管道并获得管道入口。

    与其它 IPC 不同,Binder 使用了面向对象的思想来描述作为访问接入点的 Binder 以及其在 Client 中的入口:Binder 是一个实体位于 Server 中的对象,该对象提供了一套方法用以实现对服务的请求,就像类中的成员方法一样。遍布于 Client 中的入口可以看成指向这个 Binder 对象的“指针”,一旦获取了这个“指针”就可以调用该对象的方法访问 Server。在 Client 看来,通过 Binder “指针”调用其提供的方法和通过“指针”调用其它任何本地对象的方法并无区别,尽管前者的实体位于远端 Server 中,而后者的实体位于本地内存中。“指针”是 C/C++ 中的术语,而更通常的说法是引用,即 Client 通过 Binder 的引用访问 Server。而软件领域的另一个术语“句柄”也可以用来表述 Binder 在 Client 中的存在方式。从通信的角度看,Client 中的 Binder 也可以看作是 Server 中的 Binder 实体的“代理”,在本地代表远端 Server 为 Client 提供服务。

    面向对象思想的引入将进程间通信转换为通过对某个 Binder 对象的引用调用该对象的方法,而其独特之处在于 Binder 对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中。最诱人的是,这个引用和 java 里引用一样,既可以是强类型,也可以是弱类型,而且还可以从一个进程传给其它进程,让大家都能访问同一 Server,就像将一个对象或引用赋值给另一个引用一样。Binder 模糊了进程边界,淡化了进程间的通信过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的 Binder 对象以及多个引用仿佛粘接各个应用程序的胶水,这也是 Binder 在英文中的意思---“粘合剂”;

    当然面向对象只是针对应用程序而言,对于 Binder 驱动和内核其它模块一样都是使用 C 语言实现的,没有类和对象的概念。Binder 驱动为面向对象的进程间通信方法提供底层支持。

Binder 通信模型

    Binder 框架定义了四个角色:Server、Client、Service Manager以及 Binder 驱动。其中 Server、Client、ServiceManager 运行在用户空间,Binder 驱动运行在内核空间。

如下图所示:

Binder 驱动:

    它是一种虚拟的设备驱动。虽然名叫驱动,实际上和硬件设备没有任何关系,只是实现方式和硬件设备驱动程序是一样的,比如它工作于内核态,提供 open()、mmap()、poll()、ioctl() 等标准文件操作,以字符驱动设备中的 misc 设备注册在设备目录(/dev)下,用户可以通过 /dev/binder 访问到它。Binder 驱动是连接 Server 进程、Client 进程以及 Service Manager 进程的桥梁。它的内部是通过内存映射(即调用 mmap() 函数),通过在内核空间中建立接收数据缓存区并建立 Server 进程、Client 进程以及它创建的接收数据缓存区之间的映射关系从而实现高效的跨进程通信。它也是 Binder 机制最核心的底层实现。

Server、Client、Service Manager 以及 Binder 驱动的作用以及模型原理如下图:

    从上图可以看到,实际上该 Binder 机制中只有一次数据拷贝,即 Client 进程的用户空间 -> 内核缓存区,其余的都是通过 Binder 驱动所建立的映射关系通过共享对象直接交互,从而使得 Binder 机制的传输效率高。以上就是 Android 系统中的 Binder 机制底层简单的实现过程。

注:   在使用时,Binder 驱动和 Service Manager 进程属于 Android 基础架构,即系统已经实现好了;而 Client 进程和 Server 进程属于 Android 应用层,所以需要我们自己实现。所以我们通过实现 Client 进程 和 Server 进程即可完成 Binder 机制的跨进程通信。

猜你喜欢

转载自blog.csdn.net/yz_cfm/article/details/90245193