Android Binder 原理

前言

本文基于 Android S。

Binder 是什么

Android 设计了一个轻量级的进程间通信机制,也称 远程调用机制,Binder 是这个机制中的 远程对象 的基础类。

即,Binder 对象实现了一些接口,供远程进程调用。

为了被远程进程调用,它必须遵循某种定义好的协议,这个协议为 IBinder。

同时,为了使远程进程有统一调用其方法的方式,Android 规定它实现的接口必须继承自 IInterface。

google 官网 - Binder:Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by IBinder.

Binder 是什么

其核心是如何被远程进程调用,即,其遵循的由 IBinder 接口描述的协议。这个协议由 RPC 协议衍变而来。

RPC 协议

一个通用的 RPC 框架实现如下:

通用 RPC 框架

其重点是:代理模式传输

代理模式指在 Client 使用 Server 端接口对象的代理。这样 Client 端在调用接口时,无需关注 RPC 实现,和调用本地对象的方法一样使用就可以。

RPC 框架中的代理模式

传输即 RPC 协议定义的东西。为了使两端进程能理解对方的 方法/参数,我们需要定义 序列化/反序列化打包/解包 的标准。就 Binder 而言,因为中间涉及到 Java 代码和 C++ 代码的转换,所以我们在传输之前必须把 参数/返回值类型 序列化成 JNI 可以理解的基础类型。

RPC 数据传输

Android 中封装了一个 Parcel 类来负责 序列化/反序列化 及 打包/解包。

通常 RPC 框架里面还会涉及到一个服务中心,所有意图公开给所有用户使用的服务都在里面注册,然后想使用这个服务的客户端从中取出服务来使用。

Binder 架构中的 ServiceManager#addService/ServiceManager#getService 采用的就是这个设计。

Binder RPC 架构

线程迁移

线程迁移指进程间的 IPC 看起来像是,Client 端的线程跳到 Server 端执行代码,再带着结果跳回来。

但实际上,Binder IPC 机制,并不是使用线程迁移来实现,而是采用一种模拟线程迁移的方式。

Binder 系统的用户空间代码在它运行的每个进程中维护了一个线程池,这个线程池中的线程用来处理来自其它进程的 IPC。内核模块通过以下手段模拟线程迁移:在分派 IPC 时跨进程传递线程优先级,并确保如果 IPC 递归回原始进程,由其原始线程处理。

OpenBinder 中“线程迁移”的思想

Binder IPC 机制中的 RPC 架构

先看下总的流程图:

Binder RPC 调用流程

我们可以看到 Client 端调用 Binder 代理的方法,方法和参数最后被封装后传递给了 Server 端,Server 端再把方法和参数解析出来,调用真正的 Binder 对象执行方法,再把结果返回给 Client 端。

其中一次 RPC 的来回在 Android 的 Binder 机制中被称为 transaction(事务)。

Binder 事务

其中的核心是如何把封装好的数据从 Client 端传到 Server 端。

Binder 机制中的数据传输

Binder 机制中的数据传输,是通过内存映射来实现的。这个内存映射机制由位于内核中的 Binder 驱动实现。

数据传输

可以看到映射的重点为 Server 进程中的 binder_mmap() 方法。
binder_mmap() 方法对应的代码在 binder.c 中。
其主要流程如图:

binder_mmap()

Binder 的服务中心

上面的数据传输中有个问题,即,Client 进程如何知道 Server 进程 binder_proc->buffer 所指向的物理地址空间?
答案是,通过一个服务中心:ServiceManager 进程实现。

如何查找 binder_proc-_buffer 对应的物理地址

当愿意公开自己的 Server 通过 Binder 驱动向服务中心 ServiceManager 注册自己时,Binder 驱动会做以下事情:

  1. 为这个 Server 创建其对应的 binder_node,binder_node 中包含了 binder_proc 对象;
  2. 创建与 binder_node 对应的 binder_ref,并向服务中心注册 [服务名称,binder_ref 对象]

当 Client 需要获取 Server 时,只需要使用 服务名称 通过 Binder 驱动向服务中心查找该服务名称对应的 binder_ref 对象即可。服务中心会通过 Binder 驱动把 Server 对应的 binder_ref 返回给 Client 进程。

上面过程有个问题,即,Binder 驱动是如何知道发送给它的请求是需要转交给 ServiceManager 进程的呢?
答,开机时,ServiceManager 会向 Binder 驱动设置自己为上下文管理者。其它进程只要把命令发送给上下文管理者就可以了。

如何告知 Binder 驱动请求是经 ServiceManager
创建 ServiceManager 的 BBinder,并设置为 Binder 驱动的上下文管理者

BBinder: Server 进程中,native 代码中的 本地(local) Binder;
BpBinder: Client 进程中,native 代码中的 远端(remote) Binder,即 Binder 代理;
IInterfaceImpl.Stub: Server 进程中,Java 代码中的本地 Binder;
IInterfaceImpl.Stub.Proxy: Client 进程中,Java 代码中的 Binder 代理。

开机时,ServiceManager 会向 Binder 驱动设置自己为上下文管理者,并把自己加入到 ServiceManager 的 [name, BBinder] Map 中。

ServiceManager 设置自己为 Binder 驱动的上下文管理者
在 Java 代码中获取 ServiceManager(获取 BpBinder 在 Java 层的对象)
Java 层获取 ServiceManager

具体流程如下:

Java 层获取 native ServiceManager 的 IInterface

公共 Server 向 ServiceManager 注册自己

向 ServiceManager 注册 Server

简单概括为:

  1. Server 进程新建一个要公开的 Binder,Java 层为 Stub 对象,native 层为 JavaBBinder 对象;
  2. 把 Binder 对象放入 Parcel 中;
  3. 调用 ServiceManager 的 Binder 代理(BpBinder)的 addService() 方法;
  4. 通过 Binder 驱动把请求转发给 ServiceManager 的本地 Binder(ServiceManager 的 BBinder);
  5. 从 Parcel 中解析出封装的要做为公开 Server 的 BBinder 对象,并创建其 BpBinder;
  6. 并加入到 ServiceManager 的 map 中。
Java 层添加公共 Server 流程

具体流程如下:

addService 代码流程

Client 向 ServiceManager 获取一个公共 Server

向 ServiceManager 获取 Server 代理

其主要过程和添加 Server 大致一致,唯一不一样的是,transact 后会返回从 Parcel reply 中读取出来的 BinderProxy:

Java 层获取公共 Server 流程

其具体代码流程为:

getService 代码流程
取出公开的 Server 后,调用其 BinderProxy 接口完成一个普通的 RPC 流程的例子
transact -> ontransact

匿名 Server 的获取

一个匿名 Binder Server 与实名 Server 的差异主要就在于后者是通过 Service Manager 来获取对它的引用;而前者则是以其它实名 Server 为中介来传递这一引用信息,仅此而已。另外,对于 Binder 驱动而言,只要 “路过” 它且以前没有出现过的 Binder 对象,都会被记录下来。

—— 林学森《深入理解 Android 内核设计思想》

匿名 Server 的获取

我们以 bindService 为例:

  1. 客户端应用调用 IActivityManager#bindService 时传入 IServiceConnection;
  2. IActivityManager 在做 RPC 时就创建了 IServiceConnection 的本地 Binder 和其远程代理 Binder;
  3. IActivityManager 的本地 Binder 实现者 AMS 在执行 bindService 时收到了 IServiceConnection 的 Binder 代理 BinderProxy,并通过 asInterface 接口把 BinderProxy 转换成 IServiceConnection 接口即 IServiceConnection.Stub.Proxy;
  4. AMS 启动 Service 成功后,调用 IServiceConnection#connected 把 Service.Stub 放入 Parcel;
  5. RPC 过程中会为 Service 创建本地 Binder 和 远程 Binder 代理;
  6. 客户端应用的 IServiceConnection 本地 Binder 在执行 connected() 方法时可以获得 Service Binder 代理 IInterfaceImpl.Stub.Proxy;
  7. 客户端应用通过 IInterfaceImpl.Stub.Proxy 调用 Service 的方法。

总结

Binder 是 Android 实现的远程过程调用机制中的远程对象,它提供一系列接口给远程进程使用。Server 端实现接口,Client 端调用接口。Server 端创建本地 Binder 并通过 Binder 驱动把 Binder 代理提供给 Client 端。Client 端在调用接口时把封装好的 方法名、参数、返回值、RPC 类型 放入 Server 进程的内核物理空间,因为该物理空间同时被映射到了 Server 进程的用户虚拟地址空间,所以 Server 进程的专为 RPC 调用而创建的线程可以直接从内核物理空间取出封装好的数据。数据取出后,Server 进程的本地 Binder 将数据解包,并执行其实现方法。

参考链接:

OpenBinder 官网之 Binder IPC 机制
google 官网:Binder
林学森《深入理解 Android 内核设计思想》
skywangkw:Android Binder机制(三) ServiceManager守护进程
skywangkw:Android Binder机制(一) Binder的设计和框架
小林coding:内存管理
《Linux 是怎样工作的》
gityuan:彻底理解 Android Binder 通信架构
gityuan:Binder 系列10—总结

原创文章,欢迎转载,但请注明出处。

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

推荐阅读更多精彩内容