Binder原理:

目录

Native Binder原理:

1.前置知识点:

1.1:Linux的IPC通信原理:

1.1.1:内核空间和用户空间:

1.1.2:进程隔离:

1.1.3:系统调用:

1.1.4:内存映射:

1.1.5:Linux的IPC通信原理:

1.2:Binder的IPC通信原理:

1.3:使用Binder的原因:

1.4:学习Binder的原因:

2.ServiceManager中的Binder机制:

2.1:基于Binder通信的C/S架构:

2.2:MediaServer的main函数:

2.3:每个进程唯一的ProcessState实例:

2.4:解析ServiceManager中的Binder机制:

2.4.1:BpBinder和BBinder:

2.4.2:解密IServiceManager:

2.4.3:BpServiceManager:

3.系统服务的注册过程:

3.1从调用链的角度说明MediaPlayerService的注册过程:

3.2:调用链角度的总结:

3.3:从进程的角度说明MediaPlayerService的注册过程:

3.4:进程角度的总结:

4.ServiceManager的启动过程:

4.1:打开binder设备:

4.2:注册称为Binder机制的上下文管理者:

4.3:循环等待和处理客户端发来的请求:

5.系统服务的获取过程:

5.1:客户端请求获取服务:

5.2:服务端ServiceManager处理请求:

Java Binder原理:

1.Java Binder的JNI注册:

1.1:Binder类的注册:

1.2:BinderInternal类的注册:

2.Java Binder中系统服务的注册服务:

2.1:将AMS注册到ServiceManager中:

2.2:JavaBBinder:


Native Binder原理:

根据Android系统的分层,Binder机制可以分为Java BinderNative BinderKernel Binder。其中Native Binder指的是Native层的Binder。

1.前置知识点:

1.1:Linux的IPC通信原理:

在写到Linux的进程通信原理之前,首先要了解Linux中的几个概念,而进程之间的通信简单模型如图所示:

1.1.1:内核空间和用户空间:

当我们接触到Linux时,会听到两个术语:User space(用户空间)和Kernel space (内核空间),那么它们的含义是什么呢?为了保护用户进程,不能直接操作内核,以保证内核的安全,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间。Linux操作系统将较高的1GB供内核使用,称为内核空间,将较低的3GB供各进程使用,称为用户空间。内核空间是Linux内核的运行空间,用户空间是用户程序的运行空间。为了保证内核的安全,它们是隔离的,即使用户的程序崩溃了,内核也不会受到影响。内核空间的数据是可以进程间共享的,而用户空间的数据则不可以。例如:进程A的用户空间是不能和进程B的用户空间共享的。

1.1.2:进程隔离:

进程隔离指的是一个进程不能直接操作或者访问另一个进程,也就是进程A不可以直接访问进程B的数据。

1.1.3:系统调用:

用户空间需要访问内核空间,就需要借助系统调用来实现。系统调用是用户空间访问内核空间的唯一方式,保证了所有资源访问都是在内核的控制下进行的,避免了用户对系统资源的越权访问,提升了系统的安全性和稳定性。进程A和进程B的用户空间可以通过如下系统函数和内核空间交互。

  • copy_from_user:将用户空间的数据复制到内核空间。
  • copy_to_user:将内核空间的数据复制到用户空间。
1.1.4:内存映射:

由于应用程序不能直接操作设备硬件地址,所以操作系统提供了一种机制:内存映射(Memory Map),把设备地址映射到进程虚拟内存区。例如,用户空间需要读取磁盘的文件,如果不采用内存映射,那么就需要在内核空间建立一个页缓存,页缓存去复制磁盘中的文件,然后用户空间复制页缓存的文件,这就能要两次复制。如果采用内存映射,由于建立了虚拟内存的映射,那么磁盘文件和虚拟内存的区域就可以直接映射,就少了一次复制。在Linux中通过系统调用函数mmap来实现内存的映射。将内存用户的一块区域映射到内核空间。用户对这块内存区域的修改可以直接反映到内核空间,反之亦然。内存映射能够减少数据的复制次数,实现用户空间和内核空间的高效互动。

1.1.5:Linux的IPC通信原理:

内核程序在内核空间分配内存并开辟一块内核缓存区,发送进程通过copy_from user()函数将数据复制到内核空间的缓冲区中。同样,接收进程接收数据时在自己的用户空间开辟一块内核缓存区,然后内核程序调用copy_to_user()函数将数据从内核缓存区复制到接收进程。这样数据发送进程和数据接收进程就完成了一次数据传输,也就是一次进程间的通信。Linux的IPC通信原理有以下两个问题:

  1. 一次数据传递需要经历:用户空间→内核缓存区→用户空间,需要两次数据复制,这样效率不高。
  2. 接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用API接收消息头来获取消息体的大小,以免浪费了空间或者时间。

1.2:Binder的IPC通信原理:

Binder是基于开源的OpenBinder实现的,OpenBinder最早并不是由Google公司的,而是由Be Inc公司开发的,接着由Palm Inc公司负责开发。后来OpenBindar的作者Dianme Hackbom加入了Google公司,并负责Android平台的开发工作,把这项技术也带了Android。Binder是基于内存映射来实现的,在前面我们知道内存映射通常是用在有物理介页的文件系统上的,然后Binder没有物理介质,它使用内存映射是为了跨进程传递数据的,它的的IPC通信模型如图所示:

Binder通信的步骤如下所示:

  1. Binder 驱动在内核空间创建一个数据接收缓存区。
  2. 在内核空间开辟一块内核缓存区,建立内核缓存区和数据接收缓存区之间的映射关系,以及数据接收缓存区和接收进程用户空间地址的映射关系。
  3. 发送方进程通过copy_from_user()函数将数据复制到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样就完成了一次进程之间的通信。
  4. 整个过程只使用了一次复制,不会因为不知道数据的大小而浪费空间或者时间,这样效率更高。

1.3:使用Binder的原因:

Android是基于Linux内核的,Linux提供了很多IPC机制,而Android自己却设计了Binder来进行通信,主要原因有以下四个方面:

  1. 性能方面:性能方面主要影响的因素是数据复制次数,管道、消息队列、Socket的复制次数都是两次,性能不是很好,共享内存不需要复制,性能很好,Binder的复制次数为一次,性能仅次于内存复制。
  2. 稳定性方面:Binder是基于C/S架构的,这个架构通常采用两层结构,在技术上已经很成熟了,稳定性也是没有问题的。共享内存没有分层,难以控制,并发同步访问临界资源时,可能还会产生死锁。从稳定性的角度讲,Binder是优于共享内存的。
  3. 安全方面:Android是一个开源的系统,并且拥有开放性的平台,市场上应用来源很广,因此安全性对Android平台而言极其重要。传统的IPC接收方无法获得对方可靠的进程用户ID/进程ID(UID/PID),无法鉴别对方身份。Android为每个安装好的App分配了自己的UID,通过进程的UID来鉴别进程身份。另外,Android系统中的服务端会判断UID/PID是否满足访问权限,而对外只暴露客户端,加强了系统的安全性。
  4. 语言方面:Linux是基于C语言的,C语言是面向过程的,Android应用层和Java Framework是基于Java语言的,Java语言是面向对象的。Binder本身符合面向对象的思想,因此作为Android的通信机制更合适。

从这四个方面来看,Linux提供的大部分IPC机制无法和Binder相比较,而共享内存只有在性能方面优于Binder,其他方面都劣于Binder,这就是Android要使用Binder来进行进程之间的通信的原因。当然系统中并不是所有进程之间的通信都是Binder,而是根据场景选择,例如:zygote进程和AMS通信采用的是Socket,Kill Process采用的是信号。

1.4:学习Binder的原因:

Binder机制在Android中的地位举足轻重,我们需要掌握的很多原理都和Binder有关系:

  • 系统中的各个进程是如何通信的。
  • Android系统启动过程。
  • AMS、PMS的原理。
  • 四大组件的原理,比如Activity是如何启动的。
  • 插件化原理。
  • 系统服务的客户端和服务端是如何通信的(比如MediaPlayer和MeidaPlayerService)。

以上只是列举了一小部分,简单来讲,比如系统在启动时,SystemServer进程启动会创建Binder线程池,其目的是通过Binder可以使在SystemServer进程中的服务能够和他进程间通信。再比如我们常说的AMS、PMS都是基于Binder来实现的,拿PMS来说PMS运行在SystemServer进程,如果它想要和DefaultContainerService通信(用于检查复制可移动文件的系统服务),就需要通过Binder来实现,因为DefaultContainerSemia行在com.android.defcontainer进程。还有一个比较常见的C/S 架构间通信的问题,客户端的 MediaPlayer和服务端McidaPlayerService不是运行在一个进程中的,同样需要Binder来实现通信。

2.ServiceManager中的Binder机制:

因为Java Binder是借助Native Binder来工作的,所以要先了解Native Binder。它的架构原型是基于Binder通信的C/S架构的。

2.1:基于Binder通信的C/S架构:

在Android系统中,Binder进程之间通信的使用是很普遍的,例如:MediaPlayer框架,这个框架是基于C/S架构,并且采用Binder来进行进程之间的通信。

从图中可知:除了常规C/S架构的客户端和服务端,还包括了servicemanager,它用于管理系统中的服务。首先server进程会注册一些服务到servicemanger中,如果客户要使用某个服务,则需要先到servicemanager中的查询服务的相关信息,然后根据服务的相关信息与服务所在的server进程建立通信通路,这样客户就可以使用服务了。

2.2:MediaServer的main函数:

client,server,serviceManager三者的交互都是基于Binder通信的,那么任意两者的交互都可以说明Binder通信的原理,可以说Native Binder的原理核心就是ServiceManager的原理。以MediaServer为例子。

如图所示:MediaPlayer和MediaPlayerService是通过Binder来进行通信的,MediaPlayer是客户端,MediaPlayerService是服务端,MediaPlayerService是系统多媒体的一种,系统多媒体是由MediaServer服务进程提供的,它是一个可执行程序,在Android系统启动时,MediaServer也被启动,它的入口函数主要做了:

  • 调用ProcessState的self函数获取ProcessState实例,在这个过程中会打开/dev/binder设备,并且使用mmap为Binder驱动分配一个虚拟地址空间来接收数据。
  • 调用defaultServiceManager函数获取IServiceManager,通过IServiceManager,其他的进程就可以与当前的ServiceManager交互,这里用到了Binder通信。
  • 调用MediaPlayerService::instantiate注册MediaPlayerService。

frameworks/av/media/mediaserver/main_mediaserver.cpp

2.3:每个进程唯一的ProcessState实例:

ProcessState实例代表进程的状态,先看看ProcessState实例的self函数,该函数采用了单例模式,以确保每一个进程只有一个ProcessState实例,ProcessState实例的参数为/dev/binder。而ProcessState实例的构造函数为:构造函数中调用了很多的函数,相关的有,调用open_driver函数打开了/dev/binder设备。接着调用mmap函数,它会在内核虚拟地址空间中申请一块与用户虚拟内存相同大小的内存,且申请物理内存,将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,实现了内核虚拟地址空间和用户虚拟内存空间的数据同步操作,也就是内存映射。妈mmap函数用于对Binder设备进行内存映射,除了mmap函数以外还有open函数,ioctl函数。前者打开/dev/binder设备并且返回文件操作符fd,这样就可以操作内核的Binder驱动了。后者和Binder设备进行参数的传递,这里设置的是将binder支持的最大线程数设定为15。总的来说,ProcessState实例做了:

  1. 通过mmap函数为binder分配一块虚拟地址空间,达到内存映射的目的。
  2. 打开/dev/binder设备并且设定Binder最大的支持线程数。

frameworks/native/libs/binder/ProcessState.cpp

2.4:解析ServiceManager中的Binder机制:

回到defaultServiceManager函数,它同样使用了单例模式,如果为null的话,调用interface_cast函数生成了gDefaultServiceManager,过程为:首先interface_cast函数的参数的函数内部调用了ProcessState实例的getContextObject函数。getContextObject函数中直接调用了getStrongProxyForHanadle函数,getStrongProxyForHanadle函数,它的参数值为0的话,那么handle就为0,而handle是一个资源标识。接着调用lookupHandleLocked函数查询了这个资源标识对应的资源是否存在,如果不存在的话就新建BpBinder,并且赋值给handleentry的binder,最终返回result的BpBinder。接着在interface_cast函数中返回了传入BpBinder参数的INTERACT::asInterface函数的值,该值为一个BpServiceManager,而INTERFACE的值经过替换之后为IServiceManager。所以interface_cast函数返回一个BpServiceManager,而defaultServiceManager函数也是返回一个BpServiceManager。

2.4.1:BpBinder和BBinder:

BpBinder和BBinder是Binder通信的双子星,都继承了IBinder。其中BpBinder说客户端和服务端交互的代理类,而BBinder则代表了服务端。BpBinder和BBinder是一一对应的,BpBinder会通过handle来找到对应的BBinder。

2.4.2:解密IServiceManager:

BpBinder和BBinder负责Binder的通信,而IServiceManager用于处理ServiceManager的业务,IServiceManager定义在:frameworks/native/libs/binder/include/binder/IserviceManager.h。其中IServiceManager继承了IInterface,还定义了一些的常量和一些操作service的函数,接着调用了DECLARE_META_INTERFACE宏,它的定义在IIterface.h中,该宏主要声明了一些函数和一个变量,例如:声明了asInterface函数,IServiceManager的构造函数和析构函数,其中INTERFACE的值经过替换之后为IServiceManager。而该宏的实现在IMPLEMENT_META_INTERFACE宏中,两个宏一般是配合使用的,很多的系统服务都使用了它。在这里的IMPLEMENT_META_INTERFACE宏,它的INTERFACE的值经过替换之后为IServiceManager,META值经过替换之后为android.os.IServiceManager,该宏的asInterface函数实现传入一路传过来的参数BpBinder,利用这个参数新建了一个BpServiceManager。所以interface_cast函数返回一个BpServiceManager,而defaultServiceManager函数也是返回一个BpServiceManager。

2.4.3:BpServiceManager:

BpServiceManager的作用是什么呢?先看看BpServiceManager的构造函数,BpServiceManager的构造函数调用了基类BpInterface的构造函数,其中BpInterface继承了BpRefBase,那么BpServiceManager的作用是实现了IServiceManager,并且通过BpBinder来实现通信。

3.系统服务的注册过程:

3.1从调用链的角度说明MediaPlayerService的注册过程:

上面写到MediaServer的入口函数,其中调用MediaPlayerService::instantiate注册MediaPlayerService。intantiate函数调用了defaultServiceManager的addService函数,其中defaultServiceManager函数是返回一个BpServiceManager。而addService函数中有一个data(数据包,后面的代码会不断的写入data中),还有把请求数据打包为data,再传给BpBinder的transact函数。BpBinder将逻辑交给IPCThreadState::self()的transact函数来完成,其中IPCThreadState::self()函数是用于创建IPCThreadState的,在IPCThreadState中还包含mIn和mOut,前者是接收来自Binder驱动的数据,后者是存储发送给Binder驱动的数据。而它的transact函数,包括了writeTransactionData函数和waitForResponse函数。

writeTransactionData函数:它存储了一个tr结构体binder_transaction_data,是向Binder驱动通信的数据结构。接着把handle传递给target的handle,用于标识目标,这里的目标是ServiceManager。然后对数据data进行检查,如果没有错误就赋值给对应的tr结构体。最后会将Binder请求码BC_TRANSACTION和tr结构体都写入mOut中。

waitForResponse函数中相关的代码有:调用talkWithDriver函数,该函数内部首先是binder_write_read结构体,它是和Binder驱动通信的结构体。接下来将mOut,mIn赋值给binder_write_read对应的字段,最终通过ioctl函数与Binder驱动进行通信。

注册服务的过程,传递的是BBinder对象 。服务注册过程是在服务所在进程创建binder_node,在servicemanager进程创建binder_ref。 对于同一个binder_node,每个进程只会创建一个binder_ref对象。

3.2:调用链角度的总结:

从调用链的角度来看,MediaPlayerService是如何注册的并不复杂,因为这里只是地介绍了一个调用链分支,可以总结为以下几个步骤:

  1. addService函数将数据打包发送给BpBinder来进行处理。
  2. BpBinder新建一个 IPCThreadState对象,并将通信的任务交给 IPCThreadState。
  3.  IPCThreadState的 writeTransactionData函数用于将命令协议和数据写入mOut。
  4. IPCThreadState的waitForResponse函数主要有两个作用,一个作用是通过ioctl函数操作,mOut和mIn与Binder驱动进行数据交互,另一个作用是处理各种命令协议。

3.3:从进程的角度说明MediaPlayerService的注册过程:

从图中可知:MediaPlayerService的注册过程是以C/S架构为基础的,注册过程是在MediaPlayerService进行的,它是客户端,用于请求添加系统服务。而服务端则指的是ServiceManager,用于完成系统服务的添加。客户端和服务端分别运行在两个进程中,通过Binder来进行通信。更详细点描述,就是两端通过向 Binder驱动发送命令协议来完成系统服务的添加。这其中命令协议非常多,过程也比较复杂,这里对命令协议进行了简化,只涉及了四个命令协议,其中BC_TRANSACTION 和BR_TRANSACTION 过程是一个完整的事务,BC_REPLY和BR REPLY是一个完整的事务。

3.4:进程角度的总结:

简化步骤如下所示:

  1. 客户端向 Binder驱动发送BC_TRANSACTION命令。
  2. Binder 驱动接收到请求后生成BR_TRANSACTION命令,唤醒服务端的线程后将BR_TRANSACTION命令发送给 ServiceManager。
  3. 服务端中的服务注册完成后,生成BC_REPLY命令发送给Binder驱动。
  4. Binder 驱动生成BR_REPLY命令,唤醒客户端的线程后将BR_REPLY命令发送给客户端
  5. 通过这些协议命令来驱动并且完成系统服务的注册。

4.ServiceManager的启动过程:

ServiceManager的启动过程,需要查看Kernel Binder部分的源码,这部分源码在内核源码中,需要单独下载。ServiceManager是init进程负责启动的,而init进程是在系统启动时启动的,所以ServiceManager也是如此。从Android7.0开始,对init.rc的配置文件进行了拆分,每个init.rc的配置文件服务一个rc文件。ServiceManager的启动脚本在servicemanager.rc中。

frameworks/native/cmds/servicemanager/servicemanager.rc

在启动脚本中用于通知init进程创建名字为servicemanager的进程,并且执行程序的路径为/system/bin/servicemanager。其中关键字user说明了servicemanager是以用户的身份运行的,critical说明servicemanager是系统中的关键服务,关键服务是不会退出的,如果退出了,系统就会重启。servicemanager的入口函数(main函数)在servicemanager.c中。main函数的作用有:

  1. binder_state结构体用来存储binder的三个信息。即binder设备的文件描述符,binder设备文件映射到进程的地址空间,内存映射之后,系统分配的地址空间的大小,默认为128kb。
  2. 调用binder_open函数用于打开binder设备文件,并将它映射到进程的地址空间里,且申请128kb大小的内存空间。
  3. 调用binder_become_context_manager函数,将servicemnager注册为Binder机制的上下文管理者。
  4. 调用binder_loop函数,循环等待和处理客户端发来的请求。

4.1:打开binder设备:

调用binder_open函数用于打开binder设备文件,并将它映射到进程的地址空间里,且申请128kb大小的内存空间。它首先调用了open函数打开了binder设备文件。接着调用ioctl函数获取binder的版本,如果获取不到或者内核空间和用户空间的binder不是一个版本就会直接goto到fail_open标签,释放binder的内存空间。然后调用mmap函数进行内存映射,将binder设备文件映射到进程的地址空间,映射完成之后会将地址空间的起始地址和大小保存在binderstate结构体的mapped变量和mapsize变量中。

frameworks/native/cmds/servicemanager/binder.c

4.2:注册称为Binder机制的上下文管理者:

调用binder_become_context_manager函数,将servicemnager注册为Binder机制的上下文管理者。该函数会返回ioctl函数返回的结果。ioctl函数会调用Binder驱动的binder_ioctl函数,该函数的处理部分比较多,相关的有BINDER_SET_CONTEXT_MGR的处理部分。

首先将file指针中的private_data变量赋值给binder_data变量,这个变量是一个binder_proc结构体,用于代表binder进程,管理者binder进程的各种信息。接着调用binder_get_thread函数用于获取binder_thread,其中binder_thread结构体代表的是binder线程,该函数会根据传入的binder_proc查找binder_thread,查询不到则创建一个新的binder_thread。然后全局变量binder_context_mgr_node代表Binder接着的上下文管理者对应的一个Binder对象,如果它不为null的话,就说明之前自己已经被注册为Binder的上下文管理者,Binder的上下文管理者是不能重复注册的,因此会goto到err标签。接着判断全局变量binder_context_mgr_uid(该变量代表Binder机制上下文管理者的进程有效用户的ID)的值,不为-1的话,就说明之前有进程注册Binder的上下文管理者了,而这个ID如果不等于当前用户的有效ID就goto到err标签。如果全局变量binder_context_mgr_uid除了不为-1的其他情况就说明还没有进程注册Binder机制的上下文管理者,就会将当前进程的有效用户ID赋值给全局变量binder_context_mgr_uid,并且还会调用binder_new_node函数创建一个Bidner对象赋值给全局变量binder_context_mgr_node。

4.3:循环等待和处理客户端发来的请求:

servicemanager成功注册成为Binder机制的上下文管理者之后,它需要在系统的运行期间处理客户端的请求,由于客户端的请求不知道什么时候发送,所以需要通过无限循环来实现,实现的函数为:binder_loop函数。

首先调用binder_write函数将BC_ENTER_LOOPER指令写入到Binder驱动中,这样当前的线程,也就是主线程就成为了一个Binder线程,这样就可以处理进程之间的请求了。接着无限循环的调用ioctl函数,该函数所以BINDER_WRITE_READ指令查询Binder驱动中身份有新的请求,如果有的话就交给binder_parse函数处理,如果没有新的请求,当前线程就会在Binder驱动中睡眠,等待新的进程之间的请求。

5.系统服务的获取过程:

系统服务的获取过程主要分为:客户端请求获取服务和服务端ServiceManager处理请求。

5.1:客户端请求获取服务:

想要获取MediaPlayerService,需要先调用getMediaPlayerService函数,该函数首先调用defaultServiceManager返回BpServiceManager。接着调用getService函数获取名字为“media.player”的系统服务,其返回的值是BpBinder。如果此时还没有向servicemanager注册的话,就休眠0.5s后继续调用getService函数,直到获取到对应的服务为止。然后把获取到的BpService通过interface_cast函数转换为BpMediaPlayerService,其原理是通过BpBinder的handle来找到对应的服务,即BpMediaPlayerService。

获取服务是重点,即getService函数。查询服务用到了checkService函数,该函数首先调用了data的writeString将media.player写入到data中。然后调用remote()也就是mRemote的transact函数,其中BpBinder将逻辑交给IPCThreadState::self()的transact函数来完成,其中IPCThreadState::self()函数是用于创建IPCThreadState的,在IPCThreadState中还包含mIn和mOut,前者是接收来自Binder驱动的数据,后者是存储发送给Binder驱动的数据。而它的transact函数,包括了writeTransactionData函数和waitForResponse函数。

5.2:服务端ServiceManager处理请求:

上面写到调用binder_loop函数,循环等待和处理客户端发来的请求。在该函数中无限循环的调用ioctl函数,ioctl函数所以BINDER_WRITE_READ指令查询Binder驱动中身份有新的请求,如果有的话就交给binder_parse函数处理,如果没有新的请求,当前线程就会在Binder驱动中睡眠,等待新的进程之间的请求。

这里写的是BR_TRANSACTION命令的处理部分,首先func函数传入svcmgr_handler函数,该函数在需要SVC_MGR_CHECK_SERVICE时会调用do_find_service函数,do_find_service函数会调用find_svc函数用于查询服务,返回一个svcinfo结构体,其内部包含了服务的handle值。而find_svc函数会遍历svclist列表,根据服务的名称查找对应的服务是否已经注册,如果注册就会返回对应的svcinfo,如果没有注册就返回null。其中svcinfo列表是系统服务的注册流程中,在Kernel Binder中会调用do_add_service函数,将包含服务名称和handle值的svcinfo保存到svcinfo列表中。

Java Binder原理:

Java Binder是需要借助Native Binder来进行工作的,因此Java Binder在设计上也是一个C/S架构,可以说是Java Binder是Native Binder的一个镜像。 

1.Java Binder的JNI注册:

Java Binder和Native Binder进行通信前,需要通过JNI,而JNI的注册是在Zygote进程的启动过程中进行的。具体是startReg函数用于完成虚拟机的JNI注册。在startReg函数中调用了register_jni—_procs函数,该函数的作用是循环调用gRegJNI数组,gRegJNI数组中有100多个成员变量,而成员变量REG_JNI(register_android_os_Binder),实际上就是调用参数名字对应的函数为register_android_os_Binder函数。

register_android_os_Binder函数有三个作用,分别是:注册Binder类,注册BinderInternal类,注册BinderProxy类。它们都是Java Binder关联类的一部分。

IBinder接口中定义了很多整型变量,其中定义一个叫作FLAG_ONEWAY的整形变量,客户端在发起调用时一般会阻塞,直到服务端返回结果。设置FLAG_ONEWAY后,客户端只需要把请求发送到服务端就可以立即返回,而不需要等待服务端的结果,这是一种非阻塞方式。

  • Binder和BinderProxy实现了IBinder接口,Binder是服务端的代表,而 BinderProxy是客户端的代表。
  • BinderInternal只在Binder框架中被使用,其内部类GcWatcher用于处理 Binder的垃圾回收。
  • Parcel是一个数据包装器,它可以在进程间进行传递,Parcel既可以传递基本数据类型又可以传递Binder对象,Binder通信就是通过Parcel来实现客户端与服务端数据交互的。Parcel的实现既有Java部分,又有Native部分,其具体实现在 Native部分中。

1.1:Binder类的注册:

在register_android_os_Binder函数中调用int_register_android_os_Binder函数来完成对Binder类的注册。该函数首先一个变量kBinderPathName的值为"android/os/Binder”,这是Binder在Java Binder中的全路径名。然后调用FindClassOrDie函数根据这个路径名获取Binder的Class对象,并赋值给jclass类型的变量clazz,clazz是Java层的Binder在JNI层的代表。接着调用MakeGlobalRefOrDie函数将本地引用clhazz转变为全局引用并且赋值给gBinderOffsets.mClass。接着调用GetMethodIDOrDie函数找到了Java 层的 Binder 的成员方法 execTransact并赋值给gBinderOffsets.mExecTransact。然后调用GetFieIdIDOrDie函数找到了Java层的Binder的成员变量mObject并赋值给 gBinderOffsets.mObjfet。最后通过RegisterMethodsOrDie函数注册 gBinderMethods中定义的函数,其中gBinderMethods是ININativeMethod类型的数组,其中存储的是Binder的Native方法(Java层)与JNI层函数的对应关系。

使用gBinderMethods来保存成员变量和方法有以下两个原因:

  • 为了提高效率。如果每次调用相关的方法时都需要查询方法和变量,那么显然效率比较低。
  • 这些成员变量和方法都是本地引用,在int register android os Binder 函数运回时,这些本地引用会被自动释放,因此用gBinderOffsets来保存,以便后续使用。

1.2:BinderInternal类的注册:

在register_android_os_Binder函数中调用int_register_android_os_BinderInternal函数来完成对BinderInternal类的注册。和int_register_android_os_Binder函数是实现类似,BinderInternal类主要有以下三个作用:

  •  获取BinderInternal在JNI层的代表clazz。
  •  将Binderinternal 类中有用的成员变量和方法存储到gBinderIntemalOffsets中。
  • 注册 BinderInternal类的 Native方法对应的JNI函数。

2.Java Binder中系统服务的注册服务:

Native Binder中的系统服务的注册过程,这一过程的核心是 ServiceManager,在Java Binder中,也有一个ServiceManager,然而这个 ServiceManager是Java文件。以AMS为例子注册系统服务到ServiceManager。

2.1:将AMS注册到ServiceManager中:

在AMS的setSystemProcess方法中,会调用ServiceManager的addService方法,该方法主要调用了getIServiceManager的addService方法。而getIServiceManager方法返回的是一个sServiceManager,如果为null的话则调用ServiceMangerNative的asInterface方法(该方法参数为Binder.allowBlock(BinderInternal.getContextObject())的返回值)。

其中:

  1. Binder.allowBlock的作用是将BinderProxy的sWarnOnBlocking的值设置为false。
  2. BinderInternal.getContextObject函数的作用,这是一个Native方法,找到它对应的函数android_os_BinderIntermal_getContextObject函数。该函数中首先创建了ProcessState,然后得到BpBinder。但是BpBinder是Native Binder的客户端,Java层无法直接使用,所以传入javaObjectForBinder函数来处理,其内部会创建一个BinderProxy对象,这样BinderInternal.getContextObject函数最后得到的是一个BinderProxy对象,它是Java Binder的客户端的代表。
  3. ServiceMangerNative的asInterface方法的作用是以BinderProxy作为参数来创建ServiceManagerProxy。ServiceManagerProxy是ServiceManagerNative的内部类,实现了IServiceManager接口。
  4. getIServiceManager的addService方法,因为getIServiceManager方法返回的是ServiceManagerProxy,所以也是ServiceManagerProxy的addService方法,该方法首先调用Parcel类型的data对象,将请求数据写入到data中去(Parcel类型的data对象有一个writeStrongBinder方法后续介绍)。通过mRemote的transact方法发送出去,mRemote实际上是BinderProxy。因为BinderProxy是一个native函数,所以实现函数做了将Java层的Parcel对象转换为Native层的Parcel,再从BinderProxy中获取BpBinder,最后调用BpBinder的transact函数向Binder驱动发送数据。

2.2:JavaBBinder:

Parcel类型的data对象的writeStrongBinder方法是native方法,实现的函数为android_os_Parcel_writeStrongBinder函数,它的ibinderForJavaObject函数判断如果传入的参数是Java层的BinderProxy,则返回BpBinder,如果是Java层的Bidner类,则先获取JavaBBinderHolder对象,再调用该对象的get函数。所以ServiceManagerProxy的addService方法传入的不是AMS本身,而是JavaBinder。JavaBinder继承了BBinder,当binder驱动得到了客户端的请求之后,紧接着会将响应发送给JavaBinder,这个时候就会调用JavaBinder的onTranscat函数,该函数会调用Java层Binder的execTransact函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mo@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值