GVKun编程网logo

Asop 之 Binder 机制

21

如果您对Asop之Binder机制感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于Asop之Binder机制的详细内容,并且为您提供关于Android:跟我学Binder--

如果您对Asop 之 Binder 机制感兴趣,那么本文将是一篇不错的选择,我们将为您详在本文中,您将会了解到关于Asop 之 Binder 机制的详细内容,并且为您提供关于Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?、android binder 机制、ANDROID BINDER 机制浅析、Android Binder 机制的详解的有价值信息。

本文目录一览:

Asop 之 Binder 机制

Asop 之 Binder 机制

1. Binder 简介

Android 内核也是基于 Linux 内核,先解释 Linux 中一个重要机制:IPC 。IPC 是 Inter-Process Communication 的缩写,含义即是跨进程通信。Binder 即是 Android 中最重要的 IP C机制,用一下官网上的图片,先来看下 Android 的整体架构:

图片

可以看出 Binder IPC 是 Android application 层与系统服务之间的桥梁。在 Linux 世界里跨进程通信的方式还有很多,常见的如:socket,管道,消息队列,信号量,共享内存。

Android 为什么选用 Binder 作为最重要的 IPC 机制?下面分别分析几种跨进程通信的优缺点:

  • Socket

Socket 是一种比较通用的通信方式,同一个进程(虽然可以,很少有人这么干),不同进程(zygote,Property Service),不同主机之间都可以使用 Socket,基于标准的 tcp 协议它的好处是到处通用。但 Socket 通信是一种比较复杂的通信方式,通常服务端需要开启单独的监听线程来监听从客户端发过来的数据,客户端线程发送数据给服务端,如果需要等待服务端的响应,并通过监听线程接受数据,需要进行同步,以及线程池的管理,Socket 资源的管理等,是一件很麻烦的事情。在同一个主机多进程场景来看 Socket 通信速度也不快。Socket 更多的是用于不同机器或跨网络的通信。


  • 管道

是 UNIX 系统 IPC 最古老的形式。在创建时分配一个 page 大小的内存,缓存区大小比较有限。它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。速度比较慢。


  • 消息队列

容量受到系统限制,且要第一次读的时候,要考虑上一次没有读完数据的问题。


  • 信号量

不能传递复杂消息,只能用来同步,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据,若要在进程间传递数据需要结合共享内存。

  • 共享内存

指两个或多个进程共享一个给定的存储区。能够容易控制容量,速度非常快,但要保持同步,比如写一个进程的时候,另一个进程要注意读写的问题,相当于线程中的线程安全。通常结合信号量一起使用,用来同步对共享内存的访问。

Binder 是 Android 中的一种跨进程通信方式。Binder 还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,这种通信方式在 Linux 中没有。

Binder 主要提供以下功能:

1、用驱动程序来推进进程间的通信方式 2、通过共享内存来提高性能 3、为进程请求分配每个进程的线程池,每个进程默认启动两个 Binder 服务线程 4、针对系统中的对象引入技术和跨进程对象的引用映射 5、进程间同步调用

从安全角度来看:

传统 Linux IPC 的接收方无法获得对方进程的 UID,从而无法鉴别对方身份,这对 Android 这样一个开放的操作系统来说非常致命,可能有人会说传统 IPC 可以在数据包里填入 UID,这样确实可行,但是可靠的身份标记只有由 IPC 机制本身在内核中添加。其次传统 IPC 访问接入点是开放的,无法建立私有通道。从安全角度,Binder 的安全性更高。

Android 为每个安装好的应用程序分配了自己的 UID,进程的 UID 是鉴别进程身份的重要标志,Android 系统中对外只暴露 Client 端,Client 端将任务发送给 Server 端,Server 端会根据权限控制策略,判断 UID 是否满足访问权限,从而做权限的控制。

2. 简述 Android Binder 架构

Binder 在 Android 系统中无处不在,地位非常高。App 进程的任何一处向 Service 的调用都跟 Binder 有关,在 Zygote 孵化出 system_server 进程后,在 system_server 进程中出初始化支持整个 Android framework 的各种各样的 Service,而这些 Service 从大的方向来划分,分为 Java 层 Framework 和 Native Framework 层 (C++) 的 Service,几乎都是基于 Binder IPC 机制。

Java framework:作为 Server 端继承(或间接继承)于 Binder 类,Client 端继承(或间接继承)于 BinderProxy 类。例如 ActivityManagerService(用于控制 Activity、Service、进程等)这个服务作为 Server 端,间接继承 Binder 类,而相应的 ActivityManager 作为 Client 端,间接继承于 BinderProxy 类。当然还有 PackageManagerService、WindowManagerService 等等很多系统服务都是采用 C/S 架构。

Native Framework 层:这是 C++ 层,作为 Server 端继承(或间接继承)于 BBinder 类,Client 端继承(或间接继承)于 BpBinder。例如 MediaPlayService(用于多媒体相关)作为 Server 端,继承于 BBinder 类,而相应的 MediaPlay 作为 Client 端,间接继承于 BpBinder 类。

Binder 使用面向对象的方式设计,进行一次远程过程调用就好像直接调用本地对象一样,异常方便。每一个系统服务在应用框架层都有一个 Manager 与之对应,方便开发者调用其相关功能,具体关系如下:

图片

3. IPC 原理

从进程的角度来看 IPC 机制:

每个 Android 进程,只能运行在自己的进程所拥有的虚拟地址空间,如果是32位的系统,对应一个 4GB 的虚拟地址空间,其中 3GB 是用户空,1GB 是内核空间,而内核空间的大小是可以通过参数配置的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间是可以共享的。Client 进程与 Server 进程通信,恰恰是利用进程间可共享的内核内空间来完成底层通信工作的,Client 端与 Server 端进程往往采用 ioctl 等系统调用跟内核空间的驱动进行通信。Binder 核心被实现成一个 Linux 驱动程序,并运行于内核态。这样它才能具有强大的跨进程访问能力。 

4. Binder 通信机制

a) Binder 的流程

Android 内部采用 C/S 架构。而 Binder 通信也是采用 C/S 架构。Binder 的具体流程如下:

  • 服务的提供者 Server 先向 Service Manager 注册自己的服务

  • 服务的使用者 Client 需要使用服务时向 Service Manager 申请服务

  • Client 申请成功后,即可以使用服务

在上面这个过程中 Service Manager 充当一个中介的角色,是整个 Binder 通信机制的大管家,管理着所有的服务,同时响应 Client 的请求并为之分配相应的服务(Proxy),通常情况 Client 向 Server 的调用是通过 Binder 机制跨进程调用,这里注意 Client,Server,Service Manager 这三者可能分别位于不同的进程,也就意味着 Client 和 Server 调用 Service Manager 也可能是跨进程的,这个过程也是 Binder 机制。注意这里的 Service Manager 是指 Native 层的 Service Manager(C++),并非是 framework 层的 Service Manager(Java)。b) Binder 通信整体框架Client 和 Server 存在于用户空间,Client 和 Server 实现通信是由 Binder 在内核空间实现的:

图片

Service Manager 和 Binder 驱动属于 Android 平台层,Client 和 Server 属于 Android 的应用层,开发者只需要自定义实现 Client 和 Server 端,借助于 Android 的平台架构就可以进行 IPC 通信。

Client 部分用户代码最终会调用 Binder Driver 的 transact 接口,Binder Driver 会调用 Server 的 onTransact 方法:

图片

Client 部分在实际的代码实现过程中一般不会直接调用 transact 方法(当然直接调用也是可以的),而是会借助 AIDL 自动生成接口类,同样 Server 端通常也不会直接实现 onTransact 方法,也是借助 AIDL 自动生成 Stub 接口类,Server 只需要继承这个 Stub 类实现其中的 AIDL 接口方法。关于 AIDL 全称是 Android Interface DeFinition Language,也就是 Android 接口定义语言。它是一种语言,设计这门语言的目的是为了实现进程间通信,尤其是在涉及多进程并发情况下的进程间通信。这门语言非常的简单,基本上它的语法和 Java 是一样的。 借助开发者工具集,帮我们简化了 transact 底层的序列化以及反序列化相关的一系列繁琐易错的操作,使开发者可以将精力全部集中在业务功能的开发上(AIDL 接口的实现上)。

5. Binder 内核通信协议

Biner 协议格式基本是"命令+数据",使用 ioctl (fd,cmd,arg) 系统调用函数实现与内核的交互。命令由参数 cmd 承载,数据由参数 arg,随着 cmd 不同而不同。

关于 cmd 这里只简单说一下最常用的 BINDER_WRITE_READ 命令。该命令向 Binder 写入或读取数据。参数分为两段:写部分和读部分。如果 write_size 不为0,就将 write_buffer 里的数据写入 Binder;如果 read_ size 不为0再从 Binder 中读取数据存入 read_buffer 中。write_consumered 和 read_consumered 表示操作完成时 Binder 驱动实际写入或者读出数据的个数。 

cmd 除了常用的 BINDER_WRITE_READ 还有:

BINDER_SET_MAX_THREADS 告知 Binder 驱动接收方(通常是 Server 端)线程池中最大的线程数;

BINDER_SET_CONTEXT_MGR 当前进程注册为 SM;

BINDER_TREAD_EXIT 通知 Binder 驱动当前线程退出了;

BINDER_VERSION 获取 Binder 驱动的版本号。

Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?

Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?


目录:

  • Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?

  • Android : 跟我学Binder --- (2) AIDL分析及手动实现

  • Android : 跟我学Binder --- (3) C程序示例

  • Android : 跟我学Binder --- (4) 驱动情景分析

  • Android : 跟我学Binder --- (5) C++实现

  • Android : 跟我学Binder --- (6)  JAVA实现

 

 

一、引言

  如果把Android系统比作一幅精美绝伦的画,那Binder则是其浓墨重彩的独特一笔。初步了解过的人应该知道Binder是Android核心进程间通信(IPC:Internet Process Connection)手段之一,它是基于开源的 OpenBinder 实现,OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手。从字面上来解释 Binder 有胶水、粘合剂的意思,顾名思义就是粘和不同的进程,使之实现通信。而日常开发中涉及到的如:AIDL、插件化编程技术 等等,底层通信实现都离不开Binder,所以如果想在“Android系统开发工程师”的头衔前面加上“高级”两个字,那理解、掌握Binder机制则是必经之路。

 

二、Linux 下传统的进程间通信原理

  首先了解一下 Linux IPC 相关的概念和原理有助于理解 Binder 通信原理。

  1.Linux 中跨进程通信涉及到的一些基本概念:

    进程隔离进程间不可直接相互访问资源

      简单的说就是操作系统中,进程与进程间内存是不共享的,两个进程就像两个平行的世界,A 进程没法直接访问 B 进程的数据,这就是进程隔离的通俗解释。A 进程和 B 进程之间要进行数据交互就得采用特殊的通信机制:进程间通信(IPC)。

 

    ②进程空间划分:用户空间(User Space)/内核空间(Kernel Space)

      现在操作系统都是采用的虚拟存储器,对于 32 位系统而言,它的寻址空间(虚拟存储空间)就是 2 的 32 次方,也就是 4GB。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也可以访问底层硬件设备的权限。为了保护用户进程不能直接操作内核,保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间(User Space)和内核空间(Kernel Space)。针对 Linux 操作系统而言,将最高的 1GB 字节供内核使用,称为内核空间;较低的 3GB 字节供各进程使用,称为用户空间,它们在自己的空间运行,相互隔离。

 

    ③系统调用:用户态/内核态

      虽然从逻辑上进行了用户空间和内核空间的划分,但不可避免的用户空间需要访问内核资源,比如文件操作、访问网络等等。为了突破隔离限制,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有的资源访问都是在内核的控制下进行的,避免了用户程序对系统资源的越权访问,提升了系统安全性和稳定性。当一个任务(进程)执行系统调用而陷入内核代码中执行时,称进程处于内核运行态(内核态),执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。当进程在执行用户自己的代码的时候,则称其处于用户运行态(用户态)。系统调用主要通过如下两个函数来实现,在编写Linux设备驱动程序时经常用到:

      copy_from_user() //将数据从用户空间拷贝到内核空间
      copy_to_user() //将数据从内核空间拷贝到用户空间

  2.Linux 下的传统 IPC 通信原理:

    Linux的IPC通常做法是消息发送方将要发送的数据存放在内存缓存区中,通过系统调用进入内核态。然后内核程序在内核空间分配内存,开辟一块内核缓存区,调用 copy_from_user() 函数将数据从用户空间的内存缓存区拷贝到内核空间的内核缓存区中。同样的,接收方进程在接收数据时在自己的用户空间开辟一块内存缓存区,然后内核程序调用 copy_to_user() 函数将数据从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输。

  但是这种传统的 IPC 通信方式有两个显著缺点:

  (1)一次数据传递需要经历:内存缓存区 --> 内核缓存区 --> 内存缓存区,需要 2 次数据拷贝,效率低,可能此时会想到共享内存方式,但是其难于控制不稳定;

  (2)接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用 API 接收消息头来获取消息体的大小,对于手机这种资源紧张的嵌入式移动设备来说无疑是巨大负担;

  可见传统IPC效率低,占资源,除此之外还有安全、稳定性等缺点不足以胜任Android的核心进程间通信方式,下面正式介绍Binder机制,看其有何过人之处。

 

三、为何要用Binder通信机制?

  Android系统的内核Linux已经有很多进程间通信的方式,比如:管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)等 IPC 机制,那Android为何还要再实现一个Binder IPC 呢?主要是基于高效性稳定性安全性几方面原因。

  1.高效对于手机移动通信设备来说,是基于Client-Server即C/S架构的通信方式,而上面提到的Linux传统IPC中只有socket支持Client-Server的通信方式,但是socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信。消息队列和管道采用存储-转发方式,即数据先从发送方缓存区拷贝到内核开辟的缓存区中,然后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程。共享内存虽然无需拷贝,但控制复杂,难以使用。

IPC方式 数据拷贝次数
共享内存 0
Binder 1
Socket/管道/消息队列 2

  2.稳定性Binder 基于 C/S 架构,客户端(Client)有什么需求就丢给服务端(Server)去完成,架构清晰、职责明确又相互独立,自然稳定性更好。所以从稳定性的角度讲,Binder 机制是也优于内存共享的复杂控制缺点。

  3.安全Android 作为一个开放性的平台,市场上有各类海量的应用供用户选择安装,因此安全性对于 Android 平台而言极其重要。作为用户当然不希望我们下载的 APP 偷偷读取我的通信录,上传我的隐私数据,后台偷跑流量、消耗手机电量。传统的 IPC 没有任何安全措施,完全依赖上层协议来确保。首先传统的 IPC 接收方无法获得对方可靠的进程用户ID/进程ID(UID/PID),从而无法鉴别对方身份。Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用。可靠的身份标识只有由 IPC 机制在内核中添加。其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。同时 Binder 既支持实名 Binder,又支持匿名 Binder,安全性高。

    

  Binder优势总结:

优势 描述
性能 只需要一次数据拷贝,性能上仅次于共享内存
稳定性 基于 C/S 架构,职责明确、架构清晰,因此稳定性好
安全性 为每个 APP 分配 UID,进程的 UID 是鉴别进程身份的重要标志

 

 

  另外上面讲过Linux IPC的通信原理,现在正式介绍 Binder IPC 的通信原理

  正如前面所说,跨进程通信是需要内核空间做支持的。传统的 IPC 机制如管道、Socket 都是内核的一部分,因此通过内核支持来实现进程间通信自然是没问题的。但是 Binder 并不是 Linux 系统内核的一部分,那怎么办呢?这就得益于 Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制;模块是具有独立功能的程序,它可以被单独编译,但是不能独立运行。它在运行时被链接到内核作为内核的一部分运行。这样,Android 系统就可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信,此内核模块就叫 Binder 驱动(Binder Dirver)。

  那么在 Android 系统中用户进程之间是如何通过这个内核模块(Binder 驱动)来实现通信的呢?难道是和前面说的传统 IPC 机制一样,先将数据从发送方进程拷贝到内核缓存区,然后再将数据从内核缓存区拷贝到接收方进程,通过两次拷贝来实现吗?显然不是,否则也不会有开篇所说的 Binder 在性能方面的优势了。这就不得说到 Linux 下的另一个概念:内存映射(mmap):将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间,内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。

  但是 mmap() 通常是用在有物理介质的文件系统上的,比如对flash的操作,因为进程中的用户区域是不能直接和物理设备打交道的,如果想要把flash上的数据读取到进程的用户区域,需要两次拷贝(flash-->内核空间-->用户空间),通常在这种场景下 mmap() 就能发挥作用,通过在物理介质和用户空间之间建立映射,减少数据的拷贝次数,用内存读写取代I/O读写,提高文件读取效率。而 Binder 并不存在物理介质,因此 Binder 驱动使用 mmap() 并不是为了在物理介质和用户空间之间建立映射,而是用来在内核空间创建数据接收的缓存空间。mmap()使用方法如下:

/*fd描述为打开的/dev/binder文件句柄;
 *mapsize为映射内存区域的大小;
 *mapped为映射到用户空间的内存起始地址;
 */
 bs->fd = open("/dev/binder", O_RDWR);
 bs->mapsize = mapsize;
 bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

 一次完整的 Binder IPC 通信过程通常是这样:

  ①首先 Binder 驱动在内核空间创建一个数据接收缓存区;

  ②接着在内核空间开辟一块内核缓存区,建立内核缓存区内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区接收进程用户空间地址的映射关系;

  ③发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间;

 

四、Binder通信模型实现

  通过介绍、对比Android Binder IPC和Linux 传统IPC之间的差异,Binder IPC的各优点已经凸显出来。一次完整的进程间通信必然至少包含两个进程,通常我们称通信的双方分别为客户端进程(Client)和服务端进程(Server),由于进程隔离机制的存在,通信双方必然需要借助 Binder 来实现。接下来看看实现层面是如何设计的?

 1. Client/Server/ServiceManager/驱动

  前面介绍过,Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。其中 Service Manager 和 Binder 驱动由系统提供,而 Client、Server 由应用程序来实现。Client、Server 和 ServiceManager 均是通过系统调用 open、mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。

  Client、Server、ServiceManager、Binder 驱动这几个组件在通信过程中扮演的角色就如同互联网中服务器(Server)、客户端(Client)、DNS域名服务器(ServiceManager)以及路由器(Binder 驱动)之前的关系。通常我们访问一个网页的步骤是这样的:首先在浏览器输入一个地址,如 www.google.com 然后按下回车键。但是并没有办法通过域名地址直接找到我们要访问的服务器,因此需要首先访问 DNS 域名服务器,域名服务器中保存了 www.google.com 对应的 ip 地址 10.249.23.13,然后通过这个 ip 地址才能放到到 www.google.com 对应的服务器。下面是Binder类比介绍:

Binder 驱动
  Binder 驱动就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立,Binder 在进程之间的传递,Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。 

ServiceManager 与实名 Binder
  ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了有 IP 地址外还有自己的网址。Server 创建了 Binder,并为它起一个字符形式,可读易记得名字,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“张三”的 Binder,它位于某个 Server 中。驱动为这个穿越进程边界的 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager。ServiceManger 收到数据后从中取出名字和引用填入查找表。
  细心的读者可能会发现,ServierManager 是一个进程,Server 是另一个进程,Server 向 ServiceManager 中注册 Binder 必然涉及到进程间通信。当前实现进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋!Binder 的实现比较巧妙,就是预先创造一只鸡来下蛋,ServiceManager 和其他进程同样采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 实体,其他进程都是 Client,需要通过这个 Binder 的引用来实现 Binder 的注册,查询和获取。ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用 BINDER_SET_CONTEXT_MGR 命令将自己注册成 ServiceManager 时 Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)。其次这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManager 的 Binder 通信。类比互联网,0 号引用就好比是域名服务器的地址,你必须预先动态或者手工配置好。要注意的是,这里说的 Client 是相对于 ServiceManager 而言的,一个进程或者应用程序可能是提供服务的 Server,但对于 ServiceManager 来说它仍然是个 Client。

Client 获得实名 Binder 的引用
  Server 向 ServiceManager 中注册了 Binder 以后, Client 就能通过名字获得 Binder 的引用了。Client 也利用保留的 0 号引用向 ServiceManager 请求访问某个 Binder: 我申请访问名字叫张三的 Binder 引用。ServiceManager 收到这个请求后从请求数据包中取出 Binder 名称,在查找表里找到对应的条目,取出对应的 Binder 引用作为回复发送给发起请求的 Client。从面向对象的角度看,Server 中的 Binder 实体现在有两个引用:一个位于 ServiceManager 中,一个位于发起请求的 Client 中。如果接下来有更多的 Client 请求该 Binder,系统中就会有更多的引用指向该 Binder ,就像 Java 中一个对象有多个引用一样。

至此,大致能总结出 Binder 通信过程:

  ①首先,一个进程使用 BINDER_SET_CONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;

  ②Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager,ServiceManger 将其填入查找表。

  ③Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用,通过这个引用就能实现和 Server 进程的通信。

 

 2. Binder 通信中的代理模式

  目前已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了,但是还有个问题比较困惑:A 进程想要 B 进程中某个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A 进程 没法直接使用 B 进程中的 object。

  前面介绍跨进程通信的过程都有 Binder 驱动的参与,因此在数据流经 Binder 驱动的时候驱动会对数据做一层转换。当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 objectProxy,这个 objectProxy 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己,当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了。

   到此可以对 Binder 做个更加全面的定义了:

  • 从进程间通信的角度看,Binder 是一种进程间通信的机制;
  • 从 Server 进程的角度看,Binder 指的是 Server 中的 Binder 实体对象;
  • 从 Client 进程的角度看,Binder 指的是对 Binder 代理对象,是 Binder 实体对象的一个远程代理;
  • 从传输过程的角度看,Binder 是一个可以跨进程传输的对象,Binder 驱动会对这个跨越进程边界的对象做一点点特殊处理,自动完成代理对象和本地对象之间的转换。

 

 

  可以参考该博客自测对binder机制的理解程度:https://www.jianshu.com/p/adaa1a39a274

 

-end-

android binder 机制

android binder 机制

1.Binder 通信机制介绍

这篇文章会先对比 Binder 机制与 Linux 的通信机制的差别,了解为什么 Android 会另起炉灶,采用 Binder。接着,会根据 Binder 的机制,去理解什么是 Service Manager,在 C/S 模型中扮演什么角色。最后,会从一次完整的通信活动中,去理解 Binder 通信的过程。

1.1 Android 与 Linux 通信机制的比较

虽然 Android 继承使用 Linux 的内核,但 Linux 与 Android 的通信机制不同。

在 Linux 中使用的 IPC 通信机制如下:

  1. 传统 IPC:无名 pipe, signal, trace, 有名管道

  2. AT&T Unix 系统 V:共享内存,信号灯,消息队列

  3. BSD Unix:Socket

而在 Android 中,并没有使用这些,取而代之的是 Binder 机制。Binder 机制是采用 OpenBinder 演化而来,在 Android 中使用它的原因如下:

  1. 采用 C/S 的通信模式。而在 linux 通信机制中,目前只有 socket 支持 C/S 的通信模式,但 socket 有其劣势,具体参看第二条。

  2. 有更好的传输性能。对比于 Linux 的通信机制,
  • socket:是一个通用接口,导致其传输效率低,开销大;

  • 管道和消息队列:因为采用存储转发方式,所以至少需要拷贝 2 次数据,效率低;

  • 共享内存:虽然在传输时没有拷贝数据,但其控制机制复杂(比如跨进程通信时,需获取对方进程的 pid,得多种机制协同操作)。

  • 安全性更高。Linux 的 IPC 机制在本身的实现中,并没有安全措施,得依赖上层协议来进行安全控制。而 Binder 机制的 UID/PID 是由 Binder 机制本身在内核空间添加身份标识,安全性高;并且 Binder 可以建立私有通道,这是 linux 的通信机制所无法实现的 (Linux 访问的接入点是开放的)。

综上所述,Android 采用 Binder 机制是有道理的。既然 Binder 机制这么多优点,那么我们接下来看看它是怎样通过 C/S 模型来实现的。

 

1.2Binder 在 Service 服务中的作用

 

在 android 中,有很多 Service 都是通过 binder 来通信的,比如 MediaServer 旗下包含了众多 service:

  • AudioFlinger 音频核心服务

  • AudioPolicyService:音频策略相关的重要服务

  • MediaPlayerService:多媒体系统中的重要服务

  • CameraService:有关摄像 / 照相的重要服务

Binder 在 C/S 中的流程如下:

\

  1. Server 注册服务。Server 作为众多 Service 的拥有者,当它想向 Client 提供服务时,得先去 Service Manager(以后缩写成 SM)那儿注册自己的服务。Server 可以向 SM 注册一个或多个服务。

  2. Client 申请服务。Client 作为 Service 的使用者,当它想使用服务时,得向 SM 申请自己所需要的服务。Client 可以申请一个或多个服务。

  3. 当 Client 申请服务成功后,Client 就可以使用服务了。

SM 一方面管理 Server 所提供的服务,同时又响应 Client 的请求并为之分配相应的服务。扮演的角色相当于月老,两边牵线。这种通信方式的好处是: 一方面,service 和 Client 请求便于管理,另一方面在应用程序开发时,只需为 Client 建立到 Server 的连接,就可花很少时间和精力去实 现 Server 相应功能。那么,Binder 与这个通信模式有什么关系呢?!其实,3 者的通信方式就是 Binder 机制(例如:Server 向 SM 注册服 务,使用 Binder 通信;Client 申请请求,用的是 Binder 通讯)

1.3Binder 通信机制流程 (整体框架)

\

上图即是 Binder 的通信模型。我们可以发现:

  1. Client 和 Server 是存在于用户空间

  2. Client 与 Server 通信的实现,是由 Binder 驱动在内核空间实现

  3. SM 作为守护进程,处理客户端请求,管理所有服务项。

为了方便理解,我们可以把 SM 理解成 DNS 服务器;那么 Binder Driver 就相当于路由的功能。这里就涉及到 Client 和 Server 是如何通信的问题。下面对 1.2 中提到的 3 个流程进行说明。

1.3.1 Server 向 SM 注册服务

\

  1. 首先,XXXServer (XXX 代表某个) 在自己的进程中向 Binder 驱动申请创建一个 XXXService 的 Binder 的实体,

  2. Binder 驱动为这个 XXXService 创建位于内核中的 Binder 实体节点以及 Binder 的引用,注意,是将名字和新建的引用打包传递给 SM(实体没有传给 SM),通知 SM 注册一个名叫 XXX 的 Service。

  3. SM 收到数据包后,从中取出 XXXService 名字和引用,填入一张查找表中。

  4. 此时,如果有 Client 向 SM 发送申请服务 XXXService 的请求,那么 SM 就可以在查找表中找到该 Service 的 Binder 引用,并把 Binder 引用 (XXXBpBinder) 返回给 Client。

在进一步了解 Binder 通信机制之前,我们先弄清几个概念。

  1. 引用和实体。这里,对于一个用于通信的实体(可以理解成具有真实空间的 Object),可以有多个该实体的引用(没有真实空间,可以理解成实体的 一个链接,操作引用就会操作对应链接上的实体)。如果一个进程持有某个实体,其他进程也想操作该实体,最高效的做法是去获得该实体的引用,再去操作这个引 用。

  2. 有些资料把实体称为本地对象,引用成为远程对象。可以这么理解:引用是从本地进程发送给其他进程来操作实体之用,所以有本地和远程对象之名。

1.3.2 一个问题 - 如何获得 SM 的远程接口

\

如果你足够细心,会发现这里有一个问题:

Sm 和 Server 都是进程,Server 向 SM 注册 Binder 需要进程间通信,当前实现的是进程间通信却又用到进程间通信。这就好比鸡生蛋、蛋生鸡,但至少得先有其中之一。

巧妙的 Binder 解决思路:

针对 Binder 的通信机制,Server 端拥有的是 Binder 的实体;Client 端拥有的是 Binder 的引用。
如果把 SM 看作 Server 端,让它在 Binder 驱动一运行起来时就有自己的 Binder 实体(代码中设置 ServiceManager 的 Binder 其 handle 值恒为 0)。这个 Binder 实体没有名字也不需要注册,所有的 client 都认为 handle 值为 0 的 binder 引用是用来与 SM 通信 的(代码中是这么实现的),那么这个问题就解决了。那么,Client 和 Server 中这么达成协议了(handle 值为 0 的引用是专门与 SM 通信之用 的),还不行,还需要让 SM 有 handle 值为 0 的实体才算大功告成。怎么实现的呢?!当一个进程调用 Binder 驱动时,使用 BINDER_SET_CONTEXT_MGR 命令(在驱动的 binder_ioctl 中)将自己注册成 SM 时,Binder 驱动会自动为它创建 Binder 实体。这个 Binder 的引用对所有的 Client 都为 0。

 

1.3.3 Client 从 SM 获得 Service 的远程接口

\
Server 向 SM 注册了 Binder 实体及其名字后,Client 就可以通过 Service 的名字在 SM 的查找表中获得该 Binder 的引用了 (BpBinder)。Client 也利用保留的 handle 值为 0 的引用向 SM 请求访问某个 Service:我申请访问 XXXService 的引用。 SM 就会从请求数据包中获得 XXXService 的名字,在查找表中找到该名字对应的条目,取出 Binder 的引用打包回复给 client。之 后,Client 就可以利用 XXXService 的引用使用 XXXService 的服务了。
如果有更多的 Client 请求该 Service,系统中就会有更多的 Client 获得这个引用。

1.3.4 建立 C/S 通路后

\

首先要理清一个概念:client 拥有自己 Binder 的实体,以及 Server 的 Binder 的引用;Server 拥有自己 Binder 的实体,以及 Client 的 Binder 的引用。我们也可以从接收方和发送方的方式来理解:

  • 从 client 向 Server 发数据:Client 为发送方,拥有 Binder 的实体;Server 为接收方,拥有 Binder 的引用

  • 从 server 向 client 发数据:Server 为发送方,拥有 Binder 的实体;client 为接收方,拥有 Binder 的引用。

也就是说,我们在建立了 C/S 通路后,无需考虑谁是 Client 谁是 Server,只要理清谁是发送方谁是接收方,就能知道 Binder 的实体和引用在哪边。


建立 CS 通路后的流程:(当接收方获得 Binder 的实体,发送方获得 Binder 的引用后)

  1. 发送方会通过 Binder 实体请求发送操作。

  2. Binder 驱动会处理这个操作请求,把发送方的数据放入写缓存 (binder_write_read.write_buffer) (对于接收方为读缓冲区),并把 read_size (接收方读数据) 置为数据大小(对于具体的实现后面会介绍);

  3. 接收方之前一直在阻塞状态中,当写缓存中有数据,则会读取数据,执行命令操作

  4. 接收方执行完后,会把返回结果同样用 binder_transaction_data 结构体封装,写入写缓冲区(对于发送方,为读缓冲区)

1.3.5 匿名 Binder

\

之前在介绍 Android 使用 Binder 机制的优点中,提到 Binder 可以建立点对点的私有通道,匿名 Binder 就是这种方式。在 Binder 通信中,并不是所有用来通信的 Binder 实体都需要注册给 SM 广而告之的,Server 可以通过已建立的实体 Binder 连接将创建的 Binder 实体传给 Client。而这个 Binder 没有向 SM 注册名字。这样 Server 与 Client 的通信就有很高的隐私性和安全性。

这样,整个 Binder 的通信流程就介绍完毕了,但是对于具体的代码实现(比如 binder_transaction_data 是什 么?binder_write_read.write_buffer 又是什么?具体的驱动和逻辑实现又是怎么样?),在后面章节中会一一介绍。

几点疑问:
1. 是谁,怎么样成为 SM 守护进程,handle 为 0 的 binder 实体什么时候创建?
2. binder 引用和实体是如何创建的?在驱动中如何实现的通信?
3. 在 SM 中,binder 实体是怎样转换成为引用的?
4. Server 是如何注册服务,Client 是如何获取服务的?

ANDROID BINDER 机制浅析

ANDROID BINDER 机制浅析

Binder 是 Android 上一种 IPC 机制,重要且较难理解。由于 Linux 上标准 IPC 在灵活和可靠性存在一定不足,Google 基于 OpenBinder 的设计和构想实现了 Binder。

本文只简单介绍其实现,并重点讨论 Binder 安全相关的内容。详细的 Binder 设计与实现分析,参考附录 4 篇文章。

一、Binder 实现

Android Binder 由 Client、Server、Service Manager 和 Binder 驱动 4 个部件组成,下图引自网络。

 

在类 UNIX 系统中,进程是相互独立的,一个进程不能访问另一个进程的内存空间。然而内核控制着所有进程,因此可以暴露一个接口用作 IPC。在 Binder 中这个接口就是 /dev/binder 设备,该设备由 Binder 内核驱动程序实现。

Binder 驱动是整个 Binder 架构的核心,所有 IPC 都通过它来实现。

上图的 Client 与 Server 可以看作 2 个进程,其 IPC 通过对 /dev/binder 的 ioctl 实现。从 Android 源码可以看到,ioctl 使用 binder_write_read 的结构体收发数据。

其中 write_buffer 包含驱动要执行的命令,read_buffer 包含用户层需要执行的命令:

 

binder_write 实现对 bs->fd 的 ioctl () 操作:

 

bs->fd 是打开 /dev/binder 得到的文件描述符:

 

实际数据如何在进程间传输呢?

Binder 驱动管理着每个进程的一部分地址空间。Binder 管理的内存,对于进程是只读的,其写操作由内核模块实施。当一个进程向另一个进程发送消息时,内核在目标进程的内存空间申请一部分空间,接着直接将数据从发送进程复制进去。然后它将接收消息的具体位置,通过一个短消息通知给接收进程。因为消息在接收者的内存空间,接收者可以直接访问到消息。当进程不再需要这个数据时,它通知 Binder 驱动来释放这一内存块。

可以发现,数据经过了一次复制,所以 Binder 的效率不如共享内存。

Android 中更高层次的 IPC 抽象,如 Intent(跨进程组件间传递带有关联数据的命令),Messenger(支持跨进程消息通信的对象),ContentProvider(暴露跨进程数据管理接口的组件),均是基于 Binder 实现的。

二、Binder 安全

下图展示 Binder IPC 架构的一个简单例子。

 

每个通过 Binder 框架实现 IBinder 接口,允许被访问调用的对象都可被称作 Binder 对象。对 Binder 对象的调用在一个 Binder 事务处理(transaction)内部实现,其中包括一个对目标对象的引用、需执行方法的 ID 和一个数据缓冲区。

Binder 驱动会将调用进程的 ID(PID)和有效用户 ID(EUID)自动添加到事务处理数据。被调用进程可以检查 PID 和 EUID,然后基于内部实现逻辑来决定是否执行所请求的方法。

因为 PID 和 EUID 是由内核填写的,所以调用者进程不能通过伪造身份标识来获取超出系统允许的权限。这是 Android 安全模型的核心之一,所有更高层次抽象,比如权限,均是建立在它之上。调用者的 PID 和 EUID 通过类 android.os.Binder 的 getCallingPid () 和 getCallingUid () 来访问,他们是 Android 公开 API 的一部分。

三、Binder 令牌

Binder 对象的一个重要属性是它具有跨进程、独一无二的身份标识。因此如果进程 A 创建了一个 Binder 对象,然后将之传递给进程 B,进程 B 再将之传递给进程 C,那么对这 3 个进程的调用,均由同一个 Binder 对象处理。实际上,进程 A 会通过内存地址直接引用 Binder 对象,而进程 B 和 C 使用 Binder 对象的句柄。

内核负责维护活动的 Binder 对象与它们在其他进程中的句柄之间的映射关系。因为一个 Binder 的标志符是独一无二的,并且由内核负责维护,所以用户空间进程除非通过 IPC 机制处理,否则不可能创建一个 Binder 对象的副本或获取相关引用(后面介绍如何获得 Binder 对象引用)。因此 Binder 对象是唯一的、不可伪造的、可通信的对象,可以作为安全令牌使用。

这也使得 Android 可以使用基于权能的安全模型(capability-based security)。

基于权能的安全模型中,通过赋予程序一个不可伪造的权能,授权程序对特定资源的访问权限。在 Android 中,Binder 对象可以表示权能。

在这种使用方式下,Binder 对象又被称作 Binder 令牌(Binder Token)。一个 Binder 令牌可以是一种权能,也可以是一个目标资源。一个进程拥有一个 Binder 令牌,授权该进程对 Binder 对象的完全访问控制权限,从而使其能够在目标对象上执行 Binder 事务处理操作。如果 Binder 对象实现了多个动作(基于 Binder 事务处理的 code 参数,选择要执行的动作),只要调用者拥有一个 Binder 对象的引用,就可以执行任意操作。如果需要更细粒度的访问控制,那么每个操作需要相应实现必要的权限检查,这通常利用调用者进程的 PID 和 EUID 来实现。

以上看到,获取了 Binder 对象引用(Binder 令牌)后,就获取了目标 Binder 对象的完全访问权限,如果 Binder 事务处理操作涉及敏感动作,这是危险的。

在 Android 中,通常对于 system (UID 10000) 或 root (UID 0) 权限下运行的调用进程,允许执行所有操作,而对于其他进程要执行额外的权限检查。因此控制对一个重要 Binder 对象的访问,有 2 种方式:一是通过限制谁可以获得 Binder 对象的引用,另一个是执行该 Binder 对象动作前,检查调用者的身份标志(需要 Binder 对象自己实现)。

Binder 对象可以没有其他功能实现,只被作为权能使用。这种模式下,多个协作进程可同时拥有同一个 Binder 对象,而作为服务端的进程(处理客户端请求)使用 Binder 令牌来验证它的客户端,就像 Web 服务器使用会话 Cookie 一样。

四、Binder 对象访问

出于安全考虑,Android 控制着对 Binder 对象的访问,与 Binder 对象通信的唯一方法是使用该对象的引用。但很多 Binder 对象需要被全局访问的(尤其是系统服务)。分发所有 Binder 对象到每个进程是不切实际的,所以需要一种机制,来允许进程主动发现和获取这些 Binder 对象的引用。

Android 使用 servicemanger 原生守护进程实现了这种发现机制。

servicemanger 先于系统服务启动,因此系统服务在启动时,传递服务名和一个 Binder 对象引用到 servicemanger 进行注册。一旦注册成功,任何客户端都可以通过服务名获取它的 Binder 对象引用。然而大部分系统服务有额外的权限检查,所以获得引用不能自动确保对它所有功能的访问。

同时因为在 servicemanger 上注册后,任何人都可以访问该 Binder 对象引用,所以出于安全考虑,只有一部分在白名单内的系统进程才可以注册为系统服务。

可以通过 service list 命令查看已注册系统服务列表,该命令返回每个已注册服务的名称和实现的 IBinder 接口。

 

五、附录

Android 进程间通信(IPC)机制 Binder 简要介绍和学习计划

Android 深入浅出之 Binder 机制

Android Bander 设计与实现 - 设计篇

Android Binder 设计与实现 - 实现篇

Android Binder 机制的详解

Android Binder 机制的详解

1.Binder 机制简介
Android Binder 是源于 Palm 的 OpenBinder,它为 android 设备跨进程访问而设计。Binder 机制从表现上看可以实现 java 和 native 层的对象实例可以从一个进程访问到另一个进程,从而透明的实现跨进程的调用。调用者感觉不到自己的请求是在另一个进程中完成并返回结果的。在 android 系统中使用 Binder 机制最多的是系统服务,这些服务一般运行在特定的进程中。服务的使用者成为 Client,Client 一般是应用程序,是跨进程访问的请求发起者。运行行在特定进程中的服务被成为 server,这些 server 用于接受 client 端发起的请求并处理具体相关业务并返回结果。

Android 系统中的 Binder 机制被涉及为 java 和 c++ 层两部分,使得 java 和 native 层都可以实现 Binder 的 client、server 端通过系统的 /dev/binder 虚拟设备提供跨进程发起请求和接受请求处理业务返回结果的能力。其中 java 层的 Binder 是对 C++ 层 Binder 机制的上层封装,通过 JNI 的方式调用到 C++ 的实现逻辑中。

我将通过分别分析 C++ 层的实现和 java 的封装两部分分析 Binder 机制。

2.C++ 层 Binder 的实现
我通过分析 MediaServer 的实现逻辑通过阅读源码了解一下 Binder 的实现逻辑.MediaServer 执行程序的入口是 main()方法:

.../frameworks/av/media/mediaserver/main_mediaserver.cpp
int main(int argc __unused, char** argv)
{
    .....................
    
        // 获取一个 ProcessState 对象,同时通过 pen_driver () 打开 /dev/binder 的虚拟设备,通过 mmap()为 binder 分配一块空间用于通信读写,通过
        //iotrl () 为 binder 指定线程数
        sp<ProcessState> proc(ProcessState::self());
        // 获取一个 IServerManager 对象,用于服务注册
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        // 初始化音频系统 AudioFlinger
        AudioFlinger::instantiate();
        // 多媒体服务初始化
        MediaPlayerService::instantiate();
        ResourceManagerService::instantiate();
        //cameraServer 相机服务
        CameraService::instantiate();
        // 音频系统 AudioPolicy 服务
        AudioPolicyService::instantiate();
        SoundTriggerHwService::instantiate();
        RadioService::instantiate();
        registerExtensions();
        // 创建线程池?
        ProcessState::self()->startThreadPool();
        // 加入线程池?
        IPCThreadState::self()->joinThreadPool();
        
        ....................
}

1.1ProcessState 的作用
从 main_mediaServer.cpp 的 main () 方法中可以看到 sp proc (ProcessState::self ()) 获取一个 ProcessState 对象,先看一下 ProcessState::self () 函数都做了什么:

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    // 创建一个新的 ProcessState 对象
    gProcess = new ProcessState;
    return gProcess;
}

实现很简单,self()方法就是一个简单的单例调用 new 创建了一个 ProcessState 对象。那看一下 ProcessState 的构造方法都做了些什么:

.../frameworks/native/libs/binder/ProcessState.cpp

    ProcessState::ProcessState()
    : mDriverFD (open_driver ())          // 打开 Binder
    , mVMStart (MAP_FAILED)              // 映射内存的的起始地址
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
#if !defined(HAVE_WIN32_IPC)
        // 为 Binder 分配一块内存用于存取数据
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

在 ProcessState 的构造函数中首先调用了 open_driver () 方法初始化了一个 ProcessState 的变量 mDriverFD,这个 open_driver () 方法很重要,它打开 /dev/binder 这个虚拟设备为 binder 通信做准备。在打开 binder 设备后又调用系统函数 mmap () 为打开的 binder 设备在内存中映射了一块内存空间并将内存地址返回给变量 mVMStart。

那先看一下 open_driver () 是怎么样打开 binder 驱动设备的。

.../frameworks/native/libs/binder/ProcessState.cpp
static int open_driver()
{
    // 打开 /dev/binder 虚拟设备
    int fd = open("/dev/binder", O_RDWR);
    
    ```````````````````````
    // 通过 ioctl()告诉驱动最多支持的线程数 15
    size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
    result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
    return fd;
}

在 open_driver () 中直接调用了 linux 系统函数 open () 打开了 /dev/binder 设备并为启动的 Binder 虚拟设备通过 ioctl()[系统函数] 为 binder 设备指定了最大线程数;

分析到这里基本上就是 main_mediaserver–>main () 函数中 sp proc (ProcessState::self ()) 这句代码做的事情。总结起来主要做了两件事情:

open_driver () 打开 /dev/binder 虚拟设备驱动
为打开的 Binder 虚拟设备分配内存映射空间
那下边继续回到 main_mediaserver 的 main () 方法中继续分析,看在获取到 ProcessState 后的 sp sm =defaultServiceManager () 干了什么。

defaultServerManager
defaultServerManager () 函数会返回一个实现了 IServerManager 接口的对象,通过这个对象我们可以和另外一个进程的 ServerManager 进行通信。先看一下 defaultServerManager () 方法都干了点什么:

.../frameworks/native/libs/binder/IServiceManager.cpp
sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            //gDefaultServiceManager 对象创建的地方
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}

这个方法在 IServerManager.cpp 中定义的,这个方法又是一个单例来获取一个 IServerManger 对象。内容很简单但是当我看到 interface_cast(…)后我感觉一点都不好,但是它是很重要的不过暂时先不管它。先看看 ProcessState::self ()->getContextObject (NULL)方法都干了点什么:

.../frameworks/native/libs/binder/ProcessState.cpp

    sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    // 好吧,这里直接调用了 getStrongProxyForHande () 方法。
    // 但是传入的参数 0 很重要
    return getStrongProxyForHandle(0);
}


sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);
    // 资源查找,找不到会创建一个新的对象
    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        IBinder* b = e->binder;
        // 新创建的 binder 一定为空
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
            
               ························
               
                Parcel data;
                // 这里是一次 IPC 的 binder 通信过程,暂时不分析
                // 我们现在分析的是 getStrongProxyForHander()方法返回的是什么
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
            // 创建一个 BpBinder 对象
            b = new BpBinder(handle); 
            e->binder = b;      // 填充
            if (b) e->refs = b->getWeakRefs();
            result = b;
            
            ···············
    }

    return result;
}

在 getStrongProxyForHandle()函数中首先调用了 lookupHandleLocked (0) 方法获取一个 handle_entry 类型的结构指针。lookupHandleLocked () 就是在一个 Vector 中查找对象找不到就创建一个,不重要先忽略。然后这里创建了一个 BpBinder 对象,根据名字看应该是和 Binder 机制有关的我们先可以一下 BpBinder 的构造方法:

.../frameworks/native/libs/binder/BpBinder.cpp

//BpBinder 构造函数
BpBinder::BpBinder(int32_t handle)
    : mHandle (handle)           //handle 为 0
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ```````````````
    // 另外一个重要对象?
    IPCThreadState::self()->incWeakHandle(handle);
}

在 BpBinder 的构造函数中出现了一个新对象 IPCThreadState,对象名字竟然以 IPC 应该是和 Binder 的跨进程通信又关系的。

.../frameworks/native/libs/binder/IPCThreadState.cpp

IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) {
restart:
        const pthread_key_t k = gTLS;
        //TLS = thread location storage 线程本地空间 是线程独有空间
        //getSpecific () 和 setSpecific () 可以操作这部分空间
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        // 返回一个创建的 IPCThreadState 对象
        return new IPCThreadState;
    }
    
    if (gShutdown) return NULL;
    
    ````````
    goto restart;
}

当我第一次看到这个方法时还是有点震惊的,我虽然没有学习过 C++ 但是大一那会儿学习过 C 语言。当时老师就告诉我们尽量少用 goto 这个逻辑跳转。这里竟然用了。

这个方法中 IPCThreadState* st = (IPCThreadState*) pthread_getspecific (k) 是从线程的 TLS 中获取一个 IPCThreadState 对象指针,第一次调用市肯定是不存在这个对象的所以会 new 一个 IPCThreadState 对象。

TLS 是操作系统为每个线程都单独分配的一块内存空间,当前线程独占其他线程无法访问。所以在多线程下对它的访问的线程安全的。

既然这里调用了 IPCThreadState 的构造函数我们就看看它长什么样子:

.../frameworks/native/libs/binder/IPCThreadState.cpp

IPCThreadState::IPCThreadState()
    : mProcess (ProcessState::self ()),       // 获取上边我们创建的 ProcessState 对象
      mMyThreadId(gettid()),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    // 在构造函数中将自己设置到 TLS 中
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

IPCThreadState 的构造函数很简单就是创建自己后将自己设置到当前线程的 TLS 中,下次就可以直接从当前线程的 TLS 中获取了。

我们分析了这么多其实都是 gDefaultServiceManager = interface_cast (ProcessState::self ()->getContextObject (NULL)) 的 getContextObject (null) 方法开始分析。好吧,要不是不是回到看一下我都已经不知道自己现在在分析什么了。那现在我们知道了 getContextObject () 返回的是一个 BpBinder 对象。那我们再看看 interface_cast () 设个什么东西:

.../frameworks/native/include/binder/IInterface.h
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

原来 interface_cast () 是一个定义在 IInterface.h 头文件中的模范方法,那我们现在根据我们上边的调用翻译一下这个模版方法现在的样子:

inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj) {
    return IServiceManager::asInterface(obj);
}

实话说分析到这里我有点懵了不知道怎么分析了。既然模版方法中调用了 IServiceManager::asInterface () 方法我们就先看看 IServiceManager 接口的内容:

.../frameworks/native/include/binder/IServiceManager.h

class IServiceManager : public IInterface
{
public:
    // 这个鸿定义在 IInterface.h 中,他主要做了如下工作
    //1. 定义了一个描述字符串
    //2. 定义了一个 asInterface 函数
    //3. 定义了一个 getInterfaceDescriptor 函数用于返回上边定义的描述字符串
    //4. 定义了构造函数和析构函数
    DECLARE_META_INTERFACE(ServiceManager);

    virtual sp<IBinder>         getService( const String16& name) const = 0;

    virtual sp<IBinder>         checkService( const String16& name) const = 0;

    virtual status_t            addService( const String16& name,
                                            const sp<IBinder>& service,
                                            bool allowIsolated = false) = 0;

    virtual Vector<String16>    listServices() = 0;

    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};

我们都知道 Android 系统中的系统服务像 AMS、WMS 等都是有 ServiceManager 统一管理的。这里的 IServiceManager.h 就是定义了他的功能接口。在这个接口定义中我们看到一个宏 DECLARE_META_INTERFACE (ServiceManager) 的使用,那我们先分析一下这个宏定义是什么样子:

#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();   

同样我还是先翻译一下这个宏在当前使用场景的样子:

    static const android::String16 descriptor;
    static android::sp<IServiceManager> asInterface(const android::sp<IBinder>& obj);
    virtual const android::String16 getInterfaceDescriptor() const;
    IServiceManager();
    virtual ~IServiceManager();

从对 DECLARE_META_INTERFACE 宏的翻译可以看出这是属性和方法的声明;

通过上边的对那个 IServiceManager.h 和对 DECLARE_META_INTERFACE 的翻译我们已经知道了 IServiceManager 接口定义的样子我们下面看一下他的具体实现,但是具体实现内容比较多我抽取其中重要部分分析一下:

.../frameworks/native/libs/binder/IServiceManager.cpp

 ............ 省略.............
 
 //IMPLEMENT_META_INTERFACE 这个宏是定义在 IInterface.h 文件中的
//1. 定义常量字符串 android.os.IServiceManager
//2. 实现 getInterfaceDescriptor () 函数
//3. 实现 asInterface () 函数,并返回一个 BpServiceManager 对象
IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");


status_t BnServiceManager::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    //printf("ServiceManager received: "); data.print();
    switch(code) {
        ........... 继续省略..............
    }
}

在 IServiceManager 的实现中真正让我感兴趣的这个 IMPLEMENT_META_INTERFACE 宏的使用。同样我们找找这个宏的定义并将这个宏翻译一下:

宏定义在 IInterface.h 文件中
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \

感觉当前这个宏的使用场景翻译一下:

const android::String16 IServiceManager::descriptor("android.os.IServiceManager");
const android::String16& IServiceManager::getInterfaceDescriptor() const {
    return IServiceManager::descriptor;
}
android::sp<IServiceManager> IServiceManager::asInterface(const android::sp<android::IBinder>& obj) {
    android::sp<IBinder> intr;
    if(obj != null) {
        // 这里也很重要
        intr = static_cast<IServiceManager>(obj->queryLocalInterface(IServicManager:;descriptor).get());
        if(intr == NULL) {
        // 重点看这里
            intr = new BpServieceManager(obj);
        }
    }
    return intr;
    // 构造函数我就不翻译了。这翻译真要命
}

现在我们已经翻译完了 ***IMPLEMENT_META_INTERFACE*** 宏的内容现在将翻译后内容替换到 IServiceManager.cpp 中看看

.../frameworks/native/libs/binder/IServiceManager.cpp 

 ............ 省略.............
 
 //IMPLEMENT_META_INTERFACE 这个宏是定义在 IInterface.h 文件中的
//1. 定义常量字符串 android.os.IServiceManager
//2. 实现 getInterfaceDescriptor () 函数
//3. 实现 asInterface () 函数,并返回一个 BpServiceManager 对象
<!--IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");-->

const android::String16 IServiceManager::descriptor("android.os.IServiceManager");
const android::String16& IServiceManager::getInterfaceDescriptor() const {
    return IServiceManager::descriptor;
}
android::sp<IServiceManager> IServiceManager::asInterface(const android::sp<android::IBinder>& obj) {
    android::sp<IBinder> intr;
    if(obj != null) {
        // 这里也很重要
        intr = static_cast<IServiceManager>(obj->queryLocalInterface(IServicManager:;descriptor).get());
        if(intr == NULL) {
        // 重点看这里
            intr = new BpServieceManager(obj);
        }
    }
    return intr;
    // 构造函数我就不翻译了。这翻译真要命

status_t BnServiceManager::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    //printf("ServiceManager received: "); data.print();
    switch(code) {
        ........... 继续省略..............
    }
}

这就是翻译后的 IServiceManager.cpp 的样子我们重点看一下 asInterface () 方法, 这里这个方法竟然返回了一个 BpServiceManager 对象。还记得我们先变分析中哪里调用了这个 asInterface () 方法吗?没错就是我们在 interface_cast () 模版方法调用,现在我们知道在当前分析场景下 interface_cast () 返回的是一个 ***BpServiceManager*** 类型的对象。那么问题现在有来了,还记得 interface_cast () 方法是在哪里调用的吗?没错就是 defaultServiceManager () 方法中调用的。

现在终于将 defaultServiceManager()函数分析完了,最终这个方法会返回一个 BpServiceManager 类型的对象(累死人了,分析了这么多终于知道了这个方法返回了什么。要疯了,特别想爆粗口)。那现在我是特别想知道 BpServiceManager 到底是个什么东西。但是现在不着急,defautServiceManager () 方法引发这么多的联动分析我还是先回味一下 defaultServiceManager () 函数都干了些啥:

首先调用了 ProcessState::getContextObject (null) 函数,但是这个函数什么都没有干就直接调用了自己的 getStrongProxyForHandle (0) 函数。在 getStrongProxyForHandle () 函数中创建了一个 BpBinder 对象。
在 BpBinder 构造函数中又引出了一个重要的类 IPCThreadState 类。在这里调用了 IPCThreadState::self ();
在 IPCThreadState::self () 函数中首先从线程的 TLS 中获取 IPCThreadState 对象。到那时在首次点用时 TLS 中并未存储,所以会调用它的构造方法创建一个 IPCThreadState 对象并将创建的对象保存到 TLS 中。以后就可以直接从 TLS 中获取。
interface_cast () 模版方法中调用了 IServiceManager 的 asInterface () 函数。通过我们对模版方法、DECLARE_META_INTERFACE 和 IMPLEMENT_META_INTERFACE 宏的分析翻译知道最终 asInterface () 返回了一个 BpServiceManager 对象。
在整个 defaultServiceManager () 函数中涉及到 BpBinder、ProcessState、IPCThreadState、BpServiceManager 等新的类。我们有必要分析一下目前他们的关系如何。

现在我们可以根据类图关系再来分析一下他们。首先看一下 BpServiceManager:

class BpServiceManager : public BpInterface<IServiceManager>
{
//impl 参数是 IBinder 类型,实际上就是上边创建的 BpBinder
public BpServiceManager(const sp<IBinder>& impl)
        // 调用了父类的构造方法,参数 impl 是 BpBinder
        : BpInterface<IServiceManager>(impl)
    {
    }
}

BpServiceManager 继承自 BpInterface。现在看一下 BpInterface:

模版类
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);

protected:
    virtual IBinder*            onAsBinder();
};

----------------------------
模版方法
template<typename INTERFACE>
//remote 参数是 BpBinder
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
    // 调用父类的构造方法
    : BpRefBase(remote)
{
}

继续看一下 BpRefBase 的实现:

//mRemote 最终等于 BpBinder (0) 对象
BpRefBase::BpRefBase(const sp<IBinder>& o)
// 重点在这里,Bpbinder 本传递给了 mRemote.Binder 的通信需要通过 mRemote 来完成。
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}

现在我们知道 BpServiceManager 实现了 IServiceManager 接口同时持有了 BpBinder,BpBinder 可以用于进程的通信。此时 BpServiceManager 是 ServiceManager 客户端的代理对象,BpBinder 是 Binder 通信机制的 Client 端代表。

2. MediaPlayerService addService()
我们继续回到 MediaServer 的 main () 函数中继续分析。看看 MediaPlayerService::instantiate () 实现:

void MediaPlayerService::instantiate() {
// 通过上边的分析我们已经知道 defaultServiceManager()返回的是一个 BpServiceManager 对象。在这里调用了他的 addService()方法。
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}

在 instantiate () 函数中调用了 BpServiceManager 的 addService ():

.../frameworks/native/libs/binder/IServiceManager.cpp 的内嵌类 BpServiceManager

    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        //
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        //remote () 函数返回的是 mRemote, 就是 BpRefBase 中的 mRemote,即 BpBinder 对象。这里调用了 BpBinder 的 transact () 方法。
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

从 BpServiceManager 的 addService()方法可以看出最终将调用转移到了 BpBinder 中。由此我们知道 BpBinder 才是代表 client 进行跨进程通信的代表。下面我们继续分析一下 BpBinder;

status_t BpBinder::transact(
   uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
   // Once a binder has died, it will never come back to life.
   if (mAlive) {
       //BpBinder 将通信委托给了 IPCThreadState 的 transcat()方法
       status_t status = IPCThreadState::self()->transact(
           mHandle, code, data, reply, flags);
       if (status == DEAD_OBJECT) mAlive = 0;
       return status;
   }

   return DEAD_OBJECT;
}

看到这里还是有点震惊的,我本来认为 BpBinder 已经是实现通信的客户端的代表了。现在发现它竟然将通信委托给 IPCThreadState 来完成。原来 BpBinder 还是个壳子。继续看一下 IPCThreadState::transact ():

status_t IPCThreadState::transact(int32_t handle,
                                 uint32_t code, const Parcel& data,
                                 Parcel* reply, uint32_t flags)
{
   status_t err = data.errorCheck();

   `````````````````````````````
   
   if (err == NO_ERROR) {
       LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
           (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
           // 发送 Binder 通信消息
       err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
   }
   
   if (err != NO_ERROR) {
       if (reply) reply->setError(err);
       return (mLastError = err);
   }
   
   if ((flags & TF_ONE_WAY) == 0) {
       #if 0
       if (code == 4) { // relayout
           ALOGI(">>>>>> CALLING transaction 4");
       } else {
           ALOGI(">>>>>> CALLING transaction %d", code);
       }
       #endif
       if (reply) {
           // 等待通讯结果
           err = waitForResponse(reply);
       } else {
           Parcel fakeReply;
           err = waitForResponse(&fakeReply);
       }
       
       ````````````````````````
   } else {
       err = waitForResponse(NULL, NULL);
   }
   
   return err;
}

在 IPCThreadState 的 transact () 方法中有两个重要的方法调用, err = writeTransactionData (BC_TRANSACTION, flags, handle, code, data, NULL) 和 err = waitForResponse (reply)。我们先来分析一下 writeTransactionData () 的实现:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
   int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
   binder_transaction_data tr;         //binder_transaction_data 是 binder 通信的数据结构

   tr.target.ptr = 0; /* Don''t pass uninitialized stack data to a remote process */
   tr.target.handle = handle;      // 现在 handle 是 0;0 代表 ServiceManager。
   tr.code = code;
   tr.flags = binderFlags;
   tr.cookie = 0;
   tr.sender_pid = 0;
   tr.sender_euid = 0;
   
   const status_t err = data.errorCheck();
   if (err == NO_ERROR) {
       tr.data_size = data.ipcDataSize();
       tr.data.ptr.buffer = data.ipcData();
       tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
       tr.data.ptr.offsets = data.ipcObjects();
   } else if (statusBuffer) {
       tr.flags |= TF_STATUS_CODE;
       *statusBuffer = err;
       tr.data_size = sizeof(status_t);
       tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
       tr.offsets_size = 0;
       tr.data.ptr.offsets = 0;
   } else {
       return (mLastError = err);
   }
   //mOut 的类型是 Pracel. 它是准备发送给 Binder 的 Server 端。
   mOut.writeInt32(cmd);
   // 将 Binder 通信的数据写入到 mOut 中
   mOut.write(&tr, sizeof(tr));
   
   return NO_ERROR;
}

从 writeTransactionData () 方法的分析发现里边主要做了数据准备并将数据序列化到 Pracel 中。

我们继续分析分析 waitForResponse (reply) 函数:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;
    // 循环处理
    while (1) {
        // 重点 talkWithDriver ()
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        
        cmd = (uint32_t)mIn.readInt32();
        
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;
        
        case BR_ACQUIRE_RESULT:
            {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;
        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }
            goto finish;

        default:
            // 重点
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

BR_开头的 case 都是 Binder Server 端的,BC_开头的 case 是 Binder Client 端使用。
在 IPCThreadState 的 waitForResponse () 函数中重要的方法有 talkWithDriver () 和 case 的 default:分支中的 executeCommand () 函数。现在我们先分析一下 talkWithDriver ():

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    
    binder_write_read bwr;          //binder 通信的数据结构
    
    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    // We don''t want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    // This is what we''ll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }

    IF_LOG_COMMANDS() {
        TextOutput::Bundle _b(alog);
        if (outAvail != 0) {
            alog << "Sending commands to driver: " << indent;
            const void* cmds = (const void*)bwr.write_buffer;
            const void* end = ((const uint8_t*)cmds)+bwr.write_size;
            alog << HexDump(cmds, bwr.write_size) << endl;
            while (cmds < end) cmds = printCommand(alog, cmds);
            alog << dedent;
        }
        alog << "Size of receive buffer: " << bwr.read_size
            << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
    }
    
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    // 又是一个循环
    do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(HAVE_ANDROID_OS)
        // 通过 ioctl()进行读写
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
        IF_LOG_COMMANDS() {
            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
        }
    } while (err == -EINTR);

    IF_LOG_COMMANDS() {
        alog << "Our err: " << (void*)(intptr_t)err << ", write consumed: "
            << bwr.write_consumed << " (of " << mOut.dataSize()
                        << "), read consumed: " << bwr.read_consumed << endl;
    }

    if (err >= NO_ERROR) {
        // 清空客户端的 Pracel 的内存空间
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        // 将 ioctl () 从 sever 端获取的数据写入到 mIn 中准备发送给 client 端
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        
        ````````````
        return NO_ERROR;
    }
    
    return err;
}

完成 Binder 通信其实是通过 Binder 驱动的共享内存来完成不同进程之间的通信,从对 talkWithDriver () 函数分析来看完成对 Binder 驱动的共享内存的操作是 ioctl (mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) 函数来完成的。ioctl()是一个 linux 系统 API。对 ioctl () 方法调用是在 do {} while () 中调用的,由此可见这个 ioctl () 会多次调用来完成通信。在 ioctl()完成后请求结果被放到 binder_write_read 类型的 bwr 中,然后将 bwr 的结果内容写入到 Pracel 的 mIn 中为下一步返回客户端做做准备。

我们在回到 IPCThreadState::waitForResponse()函数中分析一下另外一个 executeCommand (cmd) 方法:

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    
    switch ((uint32_t)cmd) {
    case BR_ERROR:
        result = mIn.readInt32();
        break;
        
    case BR_OK:
        break;
        
    case BR_ACQUIRE:
        ```````````
        break;
    case BR_RELEASE:
        ```````````
        break;
        
    case BR_INCREFS:
        `````````````
        break;
        
    `````````
    
    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            ALOG_ASSERT(result == NO_ERROR,
                "Not enough command data for brTRANSACTION");
            if (result != NO_ERROR) break;
            
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
            
            const pid_t origPid = mCallingPid;
            const uid_t origUid = mCallingUid;
            const int32_t origStrictModePolicy = mStrictModePolicy;
            const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;

            mCallingPid = tr.sender_pid;
            mCallingUid = tr.sender_euid;
            mLastTransactionBinderFlags = tr.flags;

            int curPrio = getpriority(PRIO_PROCESS, mMyThreadId);
            if (gDisableBackgroundScheduling) {
                if (curPrio > ANDROID_PRIORITY_NORMAL) {
                    setpriority(PRIO_PROCESS, mMyThreadId, ANDROID_PRIORITY_NORMAL);
                }
            } else {
                if (curPrio >= ANDROID_PRIORITY_BACKGROUND) {
                    set_sched_policy(mMyThreadId, SP_BACKGROUND);
                }
            }

            Parcel reply;
            status_t error;
        
            if (tr.target.ptr) {
            //BnService 从 BBinder 派生
            // 这里的 B 实际上实现了 BnServiceXXX 的对象
                sp<BBinder> b((BBinder*)tr.cookie);
                error = b->transact(tr.code, buffer, &reply, tr.flags);

            } else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }

            if ((tr.flags & TF_ONE_WAY) == 0) {
                LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                if (error < NO_ERROR) reply.setError(error);
                sendReply(reply, 0);
            } else {
                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
            }
            
            mCallingPid = origPid;
            mCallingUid = origUid;
            mStrictModePolicy = origStrictModePolicy;
            mLastTransactionBinderFlags = origTransactionBinderFlags;
            
        }
        break;
·····································
    return result;
}

看到 executeCommand()函数中那么多的 switch 分支真不知道从哪里分析,脑子已经懵懵的。还是从 BR_TRANSACTION:这个分支看看吧。在这个分支中我们可以看到 //BnService 从 BBinder 派生 sp b ((BBinder*) tr.cookie);error = b->transact (tr.code, buffer, &reply, tr.flags); 我们又看到了一个 BBinder 类的对象,有我们从 BpBinder 是 Binder Client 的代表可以猜测 BBinder 可能是 Binder 的 Server 端的代表。实际上这里的 b 是 BnSerciceManager 对象。那先放下 executeCommaond () 函数,这里我们先来分析一下 BBinder 和 BnServiceManaer 是什么然后在回来分析:

```/frameworks/native/include/binder/IServiceManager.h
class BnServiceManager : public BnInterface<IServiceManager>
{
public:
   virtual status_t    onTransact( uint32_t code,
                                   const Parcel& data,
                                   Parcel* reply,
                                   uint32_t flags = 0);
};

开始看张类图直观一些,手画丑的不行:


BnServiceManager 继承自 BnInterface, 再看一下 BnInterface 是什么:

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder
{
public:
   virtual sp<IInterface>      queryLocalInterface(const String16& _descriptor);
   virtual const String16&     getInterfaceDescriptor() const;

protected:
   virtual IBinder*            onAsBinder();
};

BnInterface 是个模版类继承自 BBinder, 在 BnServiceManager 继承 BnInterface 时模版类中的 INTERFACE 是 IServiceManager。用于 BnInterface 继承了 INTERFACE,所以 BnServicManager 继承了 ISeviceManager 接口。下面看一下 BBinder 的定义:

```/frameworks/native/include/binder/Binder.h
class BBinder : public IBinder
{
.............
}

这样以来继承关系已经很明确。再 BnServiceManager 的接口声明中重新声明了 onTransact () 函数并在 IServiceManager.cpp 中实现了它:

status_t BnServiceManager::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    //printf("ServiceManager received: "); data.print();
    switch(code) {
        case GET_SERVICE_TRANSACTION: {
            ········
        } break;
        case CHECK_SERVICE_TRANSACTION: {
            ········
        } break;
        case ADD_SERVICE_TRANSACTION: {
            CHECK_INTERFACE(IServiceManager, data, reply);
            String16 which = data.readString16();
            sp<IBinder> b = data.readStrongBinder();
            // 在这里有调用了 addService () 完成真正的服务添加。
            status_t err = addService(which, b);
            reply->writeInt32(err);
            return NO_ERROR;
        } break;
        case LIST_SERVICES_TRANSACTION: {
            ········
            return NO_ERROR;
        } break;
        default:
            // 调用父类默认实现
            return BBinder::onTransact(code, data, reply, flags);
    }
}


我们对 BnServiceManager 和 BBinder 的分析先分析到这里,回到 executeCommond () 方法中再结合 sp b ((BBinder*) tr.cookie);error = b->transact (tr.code, buffer, &reply,tr.flags); 调用 bnServiceManager 的 transact () 方法。以分析的 addService () 最终会进入到 case ADD_SERVICE_TRANSACTION: 分支当中并在这里调用了 addService () 方法。
*** 看到这里我是有点模糊的这个分支中的 addService () 方法的实现在哪里,也没有找到一个类继承自 BnServiceManager。*** 后来网上看别人的分析发现还是有个文件实现了这里的 ServiceManager 的功能,具体实现在 Service_manager.c 中实现的。

不太明白为什么不继续继承 BnServiceManager 来实现具体逻辑呢?

ServiceManager
ServiceManager 的实现在 Service_manager.c 中,先从这个文件的 main()函数开始了解一下它干了啥:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    // 打开 binder 虚拟设备
    bs = binder_open(128*1024);
    if (!bs) {
        ALOGE("failed to open binder driver\n");
        return -1;
    }
        // 角色转换
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    
    // 重点,处理客户端发过来的请求
    binder_loop(bs, svcmgr_handler);

    return 0;
}

main()函数比较简单主要通过调用了 binder_open () 函数打开 Binder 的虚拟设备。先看一下 binder_open () 都做了些什么:

/ 打开 Binder 设备
struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }
    // 打开虚拟设备,调用 Linux 系统方法打开文件
    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;
    // 调用 mmap () 函数给 binder 驱动做内存映射
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

我在这个方法中又看到了 goto 的使用。有点兴奋!

在 open_binder () 函数中打开了 Binder 驱动,并给起映射了内存空间。我们继续看一下 binder_become_context_manager (bs) 干了什么:

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

很简单但是我没有看太懂,主要是对 ioctl () 这个函数不太懂。大概是给打开的 Binder 设备指定了映射名称为 0;(谁懂给我普及一下)

ioctl 是设备驱动程序中对设备的 I/O 通道进行管理的函数。所谓对 I/O 通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的参数个数如下:int ioctl (int fd, int cmd, …);其中 fd 就是用户程序打开设备时使用 open 函数返回的文件标示符,cmd 就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和 cmd 的意义相关的。ioctl 函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对 ioctl 的支持,用户就能在用户程序中使用 ioctl 函数控制设备的 I/O 通道。百度百科

还有一个处理 Binder 请求的方法 binder_loop (bs, svcmgr_handler):

//binder_handler 是个函数指针,func 现在是 svcmgr_handler
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    // 在 binder_write 中调用了 ioctl 函数,调用 Binder 设备的函数,标志 serviceManager 进入的 Loop 状态。
    binder_write(bs, readbuf, sizeof(uint32_t));
    // 循环不断的
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        // 通过 ioctl () 方法从 Binder 设备中读取缓冲区,检查是不是又 IPC 请求。
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        // 接受到请求,然后解析结果 调用 func 函数
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

在 binder_loop () 中让 binder 设备进入 loop 状态,通过循环不断通过 ioctl()方法获取 Binder 设备是不是有 IPC 请求,如果有将获取到的数据交给 binder_parse () 函数进行解析.

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    ................. 数据处理.............................
    
        case BR_TRANSACTION: {
            
            ....................
            
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                // 调用了 func () 函数,有 func () 函数去做真正的服务管理
                res = func(bs, txn, &msg, &reply);
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
        }
        ...................................
        }
    }
    return r;
}

在 binder_prase () 方法的 binder_handler func 参数指向的是 svcmgr_handler 函数,下面看看这个函数:

.../frameworks/native/cmds/servicemanager/service_manager.c

//binder 通信处理数据的回调函数
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;

    //ALOGI("target=%p code=%d pid=%d uid=%d\n",
    //      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);

    if (txn->target.ptr != BINDER_SERVICE_MANAGER)
        return -1;

    if (txn->code == PING_TRANSACTION)
        return 0;

    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }

    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
        fprintf(stderr,"invalid id %s\n", str8(s, len));
        return -1;
    }

    if (sehandle && selinux_status_updated() > 0) {
        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
        if (tmp_sehandle) {
            selabel_close(sehandle);
            sehandle = tmp_sehandle;
        }
    }

    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
         ..........

    case SVC_MGR_ADD_SERVICE:
        // 注册服务
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        // 注册服务真正工作的地方
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
       ......
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}


在 svcmgr_handler () 函数中 SVC_ADD_SERVICES 分支中注册服务,其中注册服务的实际处理交给了 do_add_service () 方法继续处理。

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;
    
    if (!handle || (len == 0) || (len > 127))
        return -1;
    // 验证 UID 是否有添加服务的权限。
    if (!svc_can_register(s, len, spid)) {
        ALOGE("add_service(''%s'',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }
    // 检查服务是不是已经注册
    si = find_svc(s, len);
    if (si) {
        if (si->handle) {
            ALOGE("add_service(''%s'',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                 str8(s, len), handle, uid);
            svcinfo_death(bs, si);
        }
        si->handle = handle;
    } else {
        // 如果没有注册分配内存
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        //si 这个结构体
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = ''\0'';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        // 插入链表
        svclist = si;
    }

    binder_acquire(bs, handle);
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

// 存储注册服务的结构体,是个链表结构
struct svcinfo
{
    struct svcinfo *next;
    uint32_t handle;
    struct binder_death death;
    int allow_isolated;
    size_t len;
    uint16_t name[0];
};

do_add_service () 方法通过坚定要注册的服务是否有权限被注册,然后再检查服务是否已经注册了,如果没有被注册则将其插入到链表中。这样我们的服务注册工作已经完成。
C++ 层的 Binder 通信的分析基本上分析完了。想抽时间再分析一下 Java 层对 c++ 层的封装。
 

关于Asop 之 Binder 机制的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制?、android binder 机制、ANDROID BINDER 机制浅析、Android Binder 机制的详解的相关知识,请在本站寻找。

本文标签: