Android: Binder: 彻底顿悟Android Binder

Binder机制可谓是Android 知识体系的重中之中,作为偏底层的基础组件,平时我们很少关注它,但是它却无处不在,这也是android面试考察点之一,本篇将从流程上将Binder通信过一遍。

 

文章目录

1:Binder作用

2:进程与Binder驱动如何通信

3:ServiceManager进程的作用

4:进程添加服务到ServiceManager 的流程

5:进程从ServiceManager获取服务的流程

6:Binder服务端数据接收

7:Binder通信全流程图

一:Binder作用 

先看下Linux下进程地址映射关系:

我们知道:对象调用本身就是地址空间的访问。

如上:进程之间各自访问的各自内存地址,他们之间无法直接访问对方的地址,也就是说微信是不能直接调用支付宝提供的接口,但是内核具有访问其他进程地址空间的权限,因此微信可以将消息发送给内存,让内核帮忙转发给支付宝,这种方式就叫:存储/转发 方式。

基于这种方式衍生了几种 IPC(进程间通信);

1:管道,消息队列,socket等。

而Android采用了新的机制:Binder ,这种方式只需要一次数据拷贝,并且Binder更加安全。

二:进程与 Binder驱动如何通信 

既然需要内核进行消息中转,那么BInder驱动需要运行在内核空间,而事实上也的确如此,Binder驱动加载后再内核区间运行,进程只需要和Binder驱动取得联系,通过Binder驱动联系另一个进程,那么一次消息的传送过程就可以实现了。

内核提供一系列的系统调用接口给用户进程使用,当用户进程 想要访问内核时,只需要调用对应的接口,此时代码就会从用户空间切换到内核空间。

那么常见的系统调用函数接口如:

open/ read/ wirte/ioctl/ close/ mmap/ fork 等。 

用户进程与BInder通信步骤

1:打开Binder驱动: open("/dev/binder", O_RDWR| O_CLOEXEC);

2:  通过ioctl 与Binder驱动进行数据通信: ioctl(mDriverFD, BINDER_WRITE_READ,&bwr);

其中参数 bwr是读写数据结构。

三:ServiceManager进程的作用 。

先看下:Binder Client、Binder Server、ServiceManager之间关系

为了方便:ServiceManager简称为 SM。

Binder设计为C/S架构,C为Clinet(客户端),S为Server(服务端),Server端提供接口给Client使用,而这个服务是以Binder引用的形式提供的。

我们知道,C和S是不同的进程,那么C如何拿到S的Binder引用了?

你可能会说,当然是SM了,S先将Binder引用存放在SM里,当C需要的时候向SM查询即可。

这样似乎可以讲通,但是SM也是一个单独的进程,那么S,C如何与SM通信了?这个实际上就陷入了 先有鸡还是先有蛋的问题。

其实面对这样的问题,一般就是系统给初始化。什么意思?

系统先将 SM作为特殊的 Binder(handle = 0)提前放入Binder驱动,当C,S想要获取SM的Binder引用,只需要获取 handle = 0的Binder即可。

3.1 SeerviceManager注册进 Binder驱动 

 SM注册进Binder驱动后就会 等待Binder驱动的消息,这里列举两个常见的处理消息的场景

1:其他进程添加服务到 SM这里。

2:其他进程向SM插叙服务。

这里SM维护着一个链表,链表的元素是结构体

 它主要记录 name和 handle字段。

1:当SM收到添加服务的指令后,从Binder驱动里取出 handle和name ,并构造结构体插入到 链表。

2:当SM收到查询服务的指令后,从 Binder驱动里取出 name, 并找到链表里对应name的 handle, 然后写入 Binder驱动,Binder驱动最后转发给 查询进程。

4:进程添加服务到 ServiceManager的流程 

4.1:其它进程找到 SM

我们以振动服务作为切入点来分析

tranceBeginAndSlog("StartVibratorService")
vibrator = new VibratoeService(context);
ServiceManager.addService("vibrator", vibrator);
tranceEnd();

在system_server 进程里构造振动服务(VibratorService继承自Binder),并添加到SM里

system_server /  addService

 源码可以看到,主要分为两步:

1:先找到 ServiceManager

2:  往ServiceManager里添加服务 

getIserviceManager()

 

 

其中 BInderINternal.getContextObject()是 native方法。我们重点看下 Native层。

serviceManager对应的 BpBinder对象,没有找到的话,创建新的并存取缓存里,方便下次直接获取。

1:ProcessState里维护了一个单例,每个进程只有一个ProcessState对象,创建ProcessState时候就会打开Binder驱动,同时会设置Binder线程池里线程个数等其他参数。

2:Native层构造BpBinder(handle = 0) 表示该BpBinder是ServiceManager在客户端的引用,同时在构造 BinderProxyNativeData持有BpBinder。

3: 构造BinderProxy对象并持有BinderProxyNativeData, 也就是间接持有BpBinder

4: 最后构造ServiceManagerProxy对象,它实现了 IServiceManager接口,它的成员变量mRemote指向了 BinderProxy。

可以看出,获取ServiceManager的过程并不是真正去获取ServiceManager的Binder对象,而是获取它再当前进程的代理: BpBinder

4.2:  添加服务扫ServiceManager 

既然刚刚我们找到了SM的Binder代理,接下来看看我们应该如何使用它给 SM添加服务。

#ServiceManagerNative.ServiceManagerProxy
public void addService(string name,  IBinder service,boolean allowIsolated,int dumpPriority)
{

    // 构造Parcel
    Parcel data = Parcle.obtain();
    Parcel reply = Parcle.obtain();
    data.writeInterfaceToken(IServiceManager.descriptor);
    data.writeString(name);

    // 写入Binder
    data.writeStrongBinder(service);
    data.writeInt(allowIsolated ? 1:0);
    data.writeInt(dumpPriority);

   // 通过BinderProxy发送
   mRemote.transact(ADD_SERVICE_TRANSACTION, data,reply,0);
   reply.recycle();
   data.recycle();
}

 其中 IPCThreadState与线程相关,不同的线程会维护一个单例。

由此可见,最终还是通过BpBinder 发送消息,进而发送到 Binder驱动上。

此时Binder驱动收到的消息包括但不限于:

1:服务的名字

2:ServiceManager的handle

3:  BBinder对象指针

驱动建立服务handle和BBinder对象指针的映射关系,并将服务的名字和服务的 handle传递给SerivceManager(通过ServiceManager 的handle 查找)。

ServiceManger拿到消息后建立映射关系,等待其他进程的请求。

至此,进程添加服务到 ServiceManager过程已经分析完毕,可以用图表示如下:

 BBinder作用

java层传递的是Binder对象,那么如何与 Native的 BBinder关联起来了?

重点就在:下面这行代码。

Parcel.writeStrongBinder(Binder)

 

 也就是说,Server端的 java Binder对象在 Native层的代表是BBinder。

Binder驱动记录了BBinder的地址,当有消息过来时通过找到BBinder对象进而找到java层的 Binder对象,最终调用 Binder.onTransact()。

5: 进程从ServiceManager获取服务流程 

5.1:其他进程找到SM

振动服务添加完成后,某些进程想要获取振动服务,比如微信收到消息后需要振动用以提示用户。

我们看看,应该如何获取振动服务。

private void vibrate(){
    
    // 获取振动服务
    Vibrator vib = (Vibraator) getSystemService(Context.VIBRATOR_SERVICE);
    // 开始振动
    vib.virate(1000);
}

与添加服务类似,我们首先找到 SM,找到SM过程 和4.1是一样的,这里不必赘述。

5.2 从ServiceManager获取服务。

#ServiceManagerNative.ServiceManagerProxy
public IBinder getService(string name) throws RemoteException
{

    // 构造 Parcel
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IServiceManager.descriptor);
    
    // 写入名字
   data.writeString(name);
   
   // 通过BinderProxy发送
   mRemote.transact(GET_SERVICE_TRANSACTION,data, reply,0);
   IBinder binder = reply.readStrongBinder();
   reply.recycle();
   data.recycle();
   return binder;
}

由此可见,嘴周还是通过BpBinder发送消息,进而发送到 Binder驱动。

此时驱动收到的信息包括但不限于:

1:服务的名字

2: ServiceManager的 handle 

Binder驱动收到消息后,找到SM,并将服务的名字传给SM,SM从自己维护的链表找到服务名相同的节点,并取出该服务的 handle, 最后发给Binder驱动,Binder驱动将handle转发给调用者

对比添加服务和获取服务的流程,两者前半部分都是相似的,都是先拿到SM的BpBinder引用,然后写入驱动,最后由SM进程处理,只是对于获取服务来说,还需要将查询的结果handle写入驱动返回给调用方。 

5.3:handle转换成Binder对象

问: 我们知道SM维护的链表中 handle是整形的,但是调用者接收的是Binder对象,这两个是怎么结合起来的了?

答:  handle表示的是Binder服务端在客户端的索引句柄,只要客户端拿到了 handle,它就能通过Binder驱动调用到服务端。

(SM将查询到结果后将 handle写入驱动,然后从驱动将结果存入 reply字段,最后通过 reply拿到 Binder引用,重点方法是: reply.readStrongBinder())。

 

 

 通过驱动返回的handle构造 BpBinder ,最终封装为  java层的 BinderProxy。

至此,获取服务的流程就结束了,用图简化就是

6:Binder服务端数据接收 

在微信进程拿到振动服务(在system_server进程里)的Binder(BinderProxy)后,就可以调用振动方法了,然后指令发送给驱动,驱动通过振动服务的handle找到对应的服务 BBinder指针,从而调用服务的接收方法。

下面来看下system_server进程是如何接收并处理指令的。

system_server进程启动的时候,就会开启Binder线程池,并等待驱动数据到来。

当sytem_server进程添加振动服务到SM时,会将 java层的 Binder转为 Native层的BBinder,并将 BBinder对象指针写入Binder驱动。

当微信进程调用 system_server接口时:

1:微信进程调用BpBinder.transact()  将handle和数据写入Binder驱动

2: Binder驱动根据handle找到 system_server进程

3:system_server进程从驱动拿到数据,并取出BBinder指针,最终调用 到 system_server进程java层的  Binder.onTransact();

如此一来:微信程序成功调用了振动服务,也就是所一次 Client和 Service端的通信就完成了。

7:Binder通信全流程图 

 整个设计中,核心是 handle,现在我们简单的总结一下

1:通过handle构造 Client端的BpBinder(Native层),与此对应的是java层的 BinderProxy

2:通过handle驱动找到server端进程,进而调用BBinder(Native层),与此对应的是java层的Binder。

3:通过handle的一系列中转,Client,transact() 成功调用了 Server。onTransact() 一次Binder通信过程就完成了。

以上:就是整个Binder机制的梳理过程,此间省略了Binder驱动里的映射逻辑,可以将BInder驱动当做一个黑盒,更重要的是  Binder客户端和服务端是如何进行映射的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binderAndroidbinde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder跨进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行跨进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种跨进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向跨进程通信10、使用socket实现一个可以让app执行shell命令的程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值