图说Android Binder机制

2017-09-19  本文已影响0人  风雪围城

背景


  Binder机制,是Android系统跨进程协作的核心。我们知道,每个应用独立地运行在自己的进程虚拟地址空间中,就像是独自占有CPU、内存等资源,相互之间不可见。Android系统中的一个个应用,亦如一个个信息孤岛。正是Binder这样的跨进程访问机制,提供了孤岛之间沟通的桥梁。

Binder是应用之间沟通的桥梁
  如上图所示,鉴于安全和效率的考虑,Android系统服务提供是一种典型的C/S架构,如果希望获取系统服务,就必须进程IPC请求,而Binder正是数据交互的核心。
  平时做应用开发,其实很少会操心这种底层机制,但是设计思想却是通用的,为了学习它的这种思想,同时弄懂Binder机制,更好地进行应用开发,这几天找了很多资料。现在开始,才有那么点绕出来的感觉。这里,不谈源码实现,通过对各种资料的汇集,更多的以图的形式通俗展现Binder的内部机制。

Binder通信基础


  就整个通信过程,将围绕着数据传递的前提,数据如何传递,以及传递背后的协议来展开。

用户空间和内核空间---谁来中转数据

  我们知道,进程运行在虚拟地址空间,鉴于安全考虑,这块空间被分为用户空间和内核空间。其中,用户空间的执行权限较低,凡是需要访问物理设备、IO等,都需要通过系统调用的方式,由内核代码在内核空间运行。如下图所示:

用户空间和内核空间的关系
  由上图可知,每个进程通过系统调用进入内核,Linux内核空间由系统内的所有进程共享。从进程的角度看,每个进程拥有4G的虚拟空间。每个进程有各自的私有用户空间(0-3G),这个空间对系统的其他进程是不可见的。最高的1G内核空间则为所有进程以及内核所共享。这段共享的内核空间,就构成了传统Linux系统中数据不同进程之间数据交换的基础。更进一步,是依赖两个函数: 进程空间、内核空间与物理地址之间的转换关系
数据通过 copy_from_user 来到内核空间,通过copy_to_user 返回给用户空间。
由上可知,数据的传输过程至少需要对数据进行两次拷贝,但是在 Android 中又有不同,为了数据的高效传输,它对数据的拷贝仅需要一次,原因在于:Server 进程在通过 open 打开 Binder 驱动(在此处传入了进程信息,即 binder_proc),使用 mmap 进行内存映射的时候,映射出的物理内存同时存在于内核空间和 Server 所运行的 Binder 进程的用户空间。而内核空间和用户空间都是虚拟逻辑空间。这样,当数据 copy_from_user 之后存在内核空间,这页内存空间所在的实际物理空间实际上也对应于用户空间。这样,就无需在执行 copy_to_user的操作。 Android 数据跨进程传输过程
序列化与反序列化---什么样的数据完成进程穿透

  那么,什么样的数据,能够方便、安全地穿透进程间壁垒,完成进程间通信呢?
  序列化数据。在应用层,就是一个Parcel对象。
  关于序列化和反序列化,下面的这张图,描述的很清楚。

数据的转换过程
  如上图所示,我们都做过将一张纸裁剪成正方体的游戏。现在将正方体拆开,还原成平面的纸,然后通过传真机发送到远端,在远端接收后,安装纸面上的轨迹,还原这个正方体。
  实际上,这给了我们启示,在面向对象的世界里,一个复杂的对象该如何在不同进程中传递的思路,这是说我们需要把一个复杂的对象的简化了,变得平滑,转换成基础数据结构(int ,float,long等),发送到远端后,再从远端复原成复杂对象。
 public class MyParcelable implements Parcelable {
     private int mData;

     public int describeContents() {
         return 0;
     }
     //转换成Parcel对象,完成序列化
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mData);
     }
     //从Parcel转换成原对象,完成反序列化
     public static final Parcelable.Creator<MyParcelable> CREATOR
             = new Parcelable.Creator<MyParcelable>() {
         public MyParcelable createFromParcel(Parcel in) {
             return new MyParcelable(in);
         }

         public MyParcelable[] newArray(int size) {
             return new MyParcelable[size];
         }
     };
     
     private MyParcelable(Parcel in) {
         mData = in.readInt();
     }
 }
来一发服务请求---协议保证

  什么是协议?协议就是双方的约定,就是规矩,就是格式。下面,我们来看看一个实际远程调用过程的数据穿透协议。

跨进程调用
  上图实际执行了系统服务MediaPlayer的setVolume(float,float)函数调用过程,而执行的实体实际上是远端的服务进程。
  数据write_buffer实际上是通过copy_from_user传递到内核空间。传递的内容,在上图中一目了然,主要包括类(android.media.IMediaPlayer),方法(通过数字26映射),参数(1.0,1.0)。以上,就是函数调用从用户空间被传递到内核空间,通过Binder驱动发送到实际执行的远程服务进程完成执行操作。

Binder通信机制


  上面,我们该对整个通信过程有了一个大概的印象,应该已经清楚,数据从哪里来,如何穿透进程壁垒以及穿透的基础。接下来,我们先来通过下图澄清几个概念。

Binder通信的粗略过程

把上图再细致一点,放大,如下图所示:


Binder通信详细过程

Client:

  1. 首先,客户端从ContextManager中通过服务名,拿到远程服务的handler(句柄),也就是binder token。
  2. 调用远程服务的 foo 方法,然后序列化参数;
  3. 通过 transact 把调用相关的资料提交给 libbinder处理;
  4. 由 libbinder 通过ioctl进程系统调用,将foo调用请求提交给binder驱动;
  5. binder驱动通过handler找到真正的远端服务进程,然后通过ioctl函数将foo调用传递给远端服务进程的 libbinder;
  6. 远端libbinder将调用交给Stub;
  7. 在Stub中,反序列化调用信息,还原;
  8. Stub找到实际服务提供者,执行客户端的请求;
  9. 将结果序列化
    ......
    再通过Binder驱动,返回给客户端。

Server:
主要是在ContextManager中完成注册(name -> handler);等待接收Binder驱动发来的请求。

Binder限制


在使用Binder进程跨进程调用的时候,有两个重要的限制需要注意:

最后


作为一个应用开发者,对本质对底层的C实现机制,有些技痒,但是久未使用过C,想从源码的角度深入分析,需要花费更多精力,目前似乎又没有这个必要,那么,就先浅尝辄止,不求甚解。如有不对的地方,还请指出。
收获,就在于对序列化、反序列化的认识;对数据结构化的认识;从Binder框架的认识;以后再读源码的时候,不会被各种服务调用搞的晕头转向;再有就是对AIDL的使用上,不用再死记硬背如何通过AIDL进程跨进程调用了。
后面,会写一下AIDL跨进程调用过程。
本文参考链接:
https://blog.checkpoint.com/wp-content/uploads/2015/02/Man-In-The-Binder-He-Who-Controls-IPC-Controls-The-Droid-wp.pdf
https://events.linuxfoundation.org/images/stories/slides/abs2013_gargentas.pdf
https://www.dre.vanderbilt.edu/~schmidt/cs282/PDFs/android-binder-ipc.pdf
想要深入源码和数据结构的同学,可以参考以下链接:
http://gityuan.com/2015/11/01/binder-driver/
http://www.cloudchou.com/android/post-507.html
http://www.jianshu.com/p/1050ce12bc1e
http://blog.csdn.net/universus/article/details/6211589#comments
https://github.com/xdtianyu/SourceAnalysis/blob/master/Binder%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.md
或者,自己去找binder.c 和 binder.h 这两个文件来看吧。

上一篇下一篇

猜你喜欢

热点阅读