Android笔记 - Binder之基本概念

Binder 是 Android 系统引入的一种 IPC(Inter-Process Communication)方式。Binder 在 Android 系统中扮演着十分重要的角色,到处可以见到它的身影。本文主要介绍 Binder 的一些基本概念。
在 Linux 系统中,实现进程间通信方式有很多种,比如,命名管道,消息队列,信号量,socket 等。Android 最终选择 Binder 作为主要的 IPC 方式(zygote 使用 socket 作为其 IPC),主要出于以下几方面的原因:

1. Binder 能够进行安全鉴别
传统 IPC 方式没有任何安全措施,接收方无法获得对方进程可靠的 UID/PID,从而无法鉴别对方身份。Android 为每个应用程序分配了自己的 UID,并且身份标识信息由 Binder 驱动在传输过程中添加,所以 Binder 可以有效的鉴别对方身份。

2. Binder 传输效率高
socket 主要用于跨网络的进程间通讯,相对其他 IPC 方式而言,开销大,传输效率低。命名管道和消息队列采用存储-转发方式,需要经过2次内存拷贝;而Binder只需要拷贝一次内存,效率大大提升。

3. Binder 能够很好的实现 Client-Server 架构
在上述 Linux 的进程间通信方式中,只有 socket 是 Client-Server 架构,但是 socket 效率不够好,尤其是需要提供同步/异步交互时。

4. 自动化 GC
Binder 中比较容易构造出一套自动化垃圾回收机制,可以良好服务于 Java 虚拟机。

Binder 通信模型涉及到四个角色,如下图所示:
Android笔记 - Binder之基本概念_第1张图片
Binder 驱动
Binder 驱动是整个 Binder 机制的基石。由于 Android 基于 Linux 内核,应用程序都运行在用户空间中,且每个应用程序进程都有独立的地址空间。因此一般涉及到两个应用程序之间的通信时,需要通过内核进行中转,也就是通过 copy_from_user 函数将数据从发送方缓存器拷贝到内核缓存区,然后再通过 copy_to_user 函数将数据从内核缓存区拷贝到接收方缓存区。对 Android 而言,进程间通信时负责中转的就是 Binder 驱动。此外,Binder 驱动还负责线程控制,以及通信安全检查。

ServiceManager
Android 进程间通信使用 Client-Server 架构,因此 Android 系统中存在大量的 Service 组件。 ServiceManager 可以理解为 Service 上下文管理者。用于提供 Service 注册以及检索功能。实际上 ServiceManager 维护了一个系统中所有 Service 的全局链表,Client 想要获得某个 Server 提供的服务,首先需要通过 ServiceManager 获得 Service 的句柄。

Server
Android 使用了组件化的设计思想,将大量的核心服务放在不同的 Service 组件中。提供服务的组件和使用服务的组件可以运行在不同的进程中,通常把提供服务组件所在的进程称为 Server。

Service 是 Server 中运行的组件,一个 Server 中可以运行多个 Service,比如 SystemServer 中包含了 SensorService, ActivityManagerService, PackageManagerService 等多个 Service。

Client
查询和请求 Server 服务的应用程序进程。

在阅读 Binder 通信机制相关代码过程中,以上四个角色可以像坐标一样能帮助我们很好的定位,需要时刻清楚当前位于哪个角色中,从而不至于迷失在浩瀚的代码中。
Android Binder设计与实现 - 设计篇中将这四个角色的关系和互联网类比,有一种让人耳目一新的感觉。其中,Server 好比是服务器,Client 好比是客户端,ServiceManager 好比是域名服务器(DNS),Binder 驱动好比是路由器。

Binder 各角色之间的关系,可以用下图更好的说明。
Android笔记 - Binder之基本概念_第2张图片

ServiceManager 比较特殊,固定使用0号句柄值,没有 Binder 引用对象。

上图涉及到四个重要的概念。(参考自老罗的《Android系统源代码情景分析》)
Binder 本体对象
Binder 本体对象是 Service 在用户空间的存在形式,使用 BBinder 类来描述。Server 进程将一个 Binder 本地对象(Service)注册到 ServiceManager 时,Binder 驱动会为其创建一个对应的 Binder 实体对象。Binder 本地对象会被驱动程序中的 Binder 实体对象所引用。

Binder 实体对象
Binder 实体对象是 Service 在内核空间的存在形式,使用 binder_node 结构体来描述。当 Client 进程第一次引用一个 Binder 实体对象时,Binder 驱动会为它创建一个 Binder 引用对象。Binder 实体对象会被驱动程序中的 Binder 引用对象所引用。

Binder 引用对象
Binder 引用对象也存在于内核空间,使用 binder_ref 结构体来描述。Binder 引用对象被 Client 端的 Binder 代理对象所引用。

Binder 代理对象
Binder 代理对象存在于 Client 端用户空间,使用 BpBinder 类描述。Binder 代理对象都是通过一个句柄值和 Binder 引用对象关联的。Binder 代理对象通过 Binder 引用对象可以找到它所对应的 Binder 实体对象,进而找到 Binder 本地对象。

了解上述概念后,可以通过以下几个典型的通信过程来理解上图。
1. ServiceManager 成为服务管理者。ServiceManager 是用户空间中的一个守护进程。在应用程序启动时,就会和 Binder 驱动进行通信,告诉 Binder 驱动它作为服务上下文管理者。在此过程中,Binder 驱动会新建 ServiceManager 对应的 Binder 实体,并将该 Binder 实体设置为全局可访问。

2. Service 注册到 ServiceManager 中。Service 使用0号句柄向 Binder 驱动发起注册服务请求,Binder 驱动发现是注册服务,会新建一个对应的 Binder 实体对象和一个 Binder 引用对象。然后,Binder 驱动会将注册请求转发给 ServiceManager处理,ServiceManager 最终会将 Service 相关信息保存在 svclist 全局链表中,相关信息包含两部分:Service 对应的服务名以及 Binder 引用对象对应的句柄值。

3. Client 获取远程服务。Client 使用0号句柄向 Binder 驱动发起查询服务请求,Binder 驱动将该请求转发给 ServiceManager 处理。ServiceManager 收到查询服务请求后,根据 Client 发送过来的服务名从 svclist 全局链表中找到目标 Service。接下来,ServiceManager 通过 Binder 驱动将 Service 对应的句柄值返回给 Client,在此过程中会为 Client 新建一个对应的 Binder 引用对象。Client 获得服务句柄值后,就可以通过新建的 Binder 引用对象找到它所对应的 Binder 实体对象,进而找到 Binder 本地对象,从而使用远程服务。

以上是 Binder 通信机制的一些重要的基本概念,虽然忽略掉了非常多的细节,但应该可以对 Binder 有一个感性的认识。接下来会介绍 Binder 通信机制中使用到的数据结构。

参考资料:
1. Android Binder机制(一) Binder的设计和框架
2. 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

你可能感兴趣的:(android,ipc,Binder)