Android Binder通信机制简单了解

Android Binder通信机制简单了解

参考

Android图文解析 Binder跨进程通信
Android Binder之应用层总结与分析
Binder通信的一次内存拷贝
Android Bander设计与实现

Binder通信基础是Kernel的内存共享

一个进程空间分为 用户空间 & 内核空间(Kernel),即把进程内 用户 & 内核 隔离开来

  • 进程间,用户空间的数据不可共享
  • 进程间,内核空间的数据可共享
  • 进程内 用户 与 内核 进行交互 ,Linux可使用copy_from_user()和copy_to_user()实现这两个跨空间拷贝,但Binder使用的方式又有所不同(后边会说道)。

Android Binder通信机制简单了解

该图片来自:Android:图文解析 Binder跨进程通信 原理

Binder通信原理

Binder通信采用C/S架构,从组件视角来说,包含Client、Server、ServiceManager以及binder驱动。

  • 其中ServiceManager用于管理系统中的各种服务
  • Binder驱动(Kernel层)负责分配管理接收方数据缓存和数据交换(现在晕没关系,后边会说道)

Android Binder通信机制简单了解

该图片来自:Android Binder之应用层总结与分析

一、Service向ServiceManager注册服务

这里SM是一个进程,Server是另一个进程,Server向SMgr注册Binder亦是进程间通信。

SM提供的Binder比较特殊,它没有名字也不需要注册;当SM进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成SM时,Binder驱动会自动为它创建Binder实体;其次这个Binder的引用在所有Client中都固定为0而无须通过其它手段获得。

因此,一个Server若要向SM注册自己Binder便可通过0这个引用号和SM的Binder通信。

Created with Raphaël 2.1.2Service进程Service进程Binder驱动 (Kernel层)Binder驱动 (Kernel层)ServieManagerServieManager向SM注册Service通过0这个Binder引用向SM注册ServiceBinder驱动为这个Server创建位于内核中的Binder实体节点以及Binder引用将Server名字和Binder的引用传给SM存储Server名字和Binder的引用

Android Binder通信机制简单了解

该图片来自:Android Binder之应用层总结与分析

二、Client 获得Service的Binder引用

Server向SM注册了Binder引用及其名字后,Client就可以通过名字获得该Binder的引用了;

Created with Raphaël 2.1.2Client进程Client进程Binder驱动 (Kernel层)Binder驱动 (Kernel层)ServieManagerServieManager通过0这个引用号和SMgr的Binder通信获取Service端BinderProxyClient向SM发送申请Server的请求转发请求查找ServiceBinder引用返回数据返回Service的BinderProxy
  • Client也利用保留的0号引用向SM请求访问某个Binder;
  • SM收到这个连接请求,从请求数据包里获得Binder的名字,在查找表里找到该名字对应的条目,从条目中取出Binder的引用,将该引用作为回复发送给发起请求的Client。

从面向对象的角度,这个Binder对象现在有了两个引用:一个位于SMgr中,一个位于发起请求的Client中。如果接下来有更多的Client请求该Binder,系统中就会有更多的引用指向该Binder,就象java里一个对象存在多个引用一样。而且类似的这些指向Binder的引用是强类型,从而确保只要有引用Binder实体就不会被释放掉。

Android Binder通信机制简单了解

该图片来自:Android Binder之应用层总结与分析

三、Client向Service获取数据

暂且撇开Binder,考虑一下传统的IPC方式中,数据是怎样从发送端到达接收端的呢?

  • 通常的做法是,发送方将准备好的数据存放在缓存区中,调用API通过系统调用进入内核中。
  • 内核服务程序在内核空间分配内存,将数据从发送方缓存区复制到内核缓存区中。
  • 接收方读数据时也要提供一块缓存区,内核将数据从内核缓存区拷贝到接收方提供的缓存区中并唤醒接收线程,完成一次数据发送。

这种存储-转发机制需要做两次拷贝,用户空间->内核空间->用户空间,Linux使用copy_from_user()和copy_to_user()实现这两个跨空间拷贝;其次是接收数据的缓存要由接收方提供,可接收方不知道到底要多大的缓存才够用,只能开辟尽量大的空间或先调用API接收消息头获得消息体大小,再开辟适当的空间接收消息体。

Binder采用一种全新策略:
Binder驱动为进程的用户和内核空间分配了一段虚拟地址,这段虚拟地址将用于binder内存的访问。binder的物理内存页由binder驱动负责分配,然后这些物理页的访问,将分为进程的用户空间和进程内核空间。由于进程的用户空间和内核空间的虚拟地址范围是不一样的,所以这里分配的一段虚拟地址将包括进程的用户空间地址和内核空间地址。

  • 内核刚开始只是分配了一个物理页,并且分别将这个物理页映射到进程的内核虚拟地址空间V1和进程的用户虚拟地址空间V2;
  • 在用户空间访问V1和在内核空间访问V2,其实都是访问的是同一个物理内存块,从而实现进程的内核和用户空间共享同一块物理内存的目的;
  • 这样binder驱动在内核空间,将一段数据拷贝到这个物理页,则该进程的用户空间则不需要copy_to_user()即可以同步看到内核空间的修改,并能够访问这段物理内存。
Created with Raphaël 2.1.2Client进程Client进程Binder驱动 (Kernel层)Binder驱动 (Kernel层)Service进程Service进程Client与Service通信通过 Service的BinderProxy 请求数据 Parcel客户端线程挂起转发请求执行指定方法返回数据执行mmap函数为client的用户空间与Binder驱动内核空间分配同一块内存并进行Service用户空间数据到Binder驱动内核空间数据拷贝唤醒被挂起的线程并从client用户空间读取数据

Android Binder通信机制简单了解

该图片来自:Android Binder之应用层总结与分析