图说Android Binder机制

背景


  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驱动:一个内核层级的驱动,促成了进程间通信。有人把它比作网络通信中的路由器,而路由器的作用,就是从原地址存储转发数据目的地址,很贴切。
  • Binder对象:是IBinder接口的实现。实际上就是远程服务动作的实际执行者。
  • BinderToken:也有叫 handler 的。实际上就是一个32位的整型值,唯一的代表了一个远端Binder对象。
  • Binder Service:实际持有Binder对象,处理业务逻辑。
  • Binder Client:请求Binder服务者。
  • Proxy:实现了AIDL接口,能够序列化/反序列化数据结构,同时能够通过Binder引用进程远程调用。
  • Stub:能够序列化和反序列化数据,并把转换过程映射到实际的服务端的方法调用。
  • Context Manager:一个注册 handler(句柄)为0的特殊Binder对象。通过name到handler的映射关系,注册和查找别的Binder对象。也有人把它对应为DNS服务器。DNS服务器的作用,就是通过IP地质到域名的映射,提供查找和注册。即我们向DNS服务器注册自己的IP,并把它映射到一个域名;这样,别人就可以通过域名来访问我们的主机,因为DNS服务器将把这个域名转换为IP。

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


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进程跨进程调用的时候,有两个重要的限制需要注意:

  • 一个服务进程中最多同时支持15个binder线程处理请求;
  • 一个进程中用户交互穿透数据的缓存大小最多为1M,这就意味着,在传递的数据是有限的,如果资源耗尽,会抛出异常。

最后


作为一个应用开发者,对本质对底层的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 这两个文件来看吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容