前言
Android系统中,每个应用程序是由Android的Activity,Service,Broadcast,ContentProvider这四剑客的中一个或多个组合而成,这四剑客所涉及的多进程间的通信底层都是依赖于Binder IPC机制。
Linux里几种比较常见的IPC方式(Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信)比如有:共享内存,信号,Socket,管道(pipe),消息队列等。(具体分析见:http://blog.youkuaiyun.com/zcxwww/article/details/51208190)
在Android中最有特色的进程间通信方式就是Binder了,通过Binder可以轻松地实现进程间通信。
Binder实现服务器进程与客户机进程之间通信的架构图如下:
Binder机制下的两个进程之间的通信基本就是上面那样一个过程:即服务端定义服务,客户端通过获取服务端的对象引用的方式(通过中介Binder驱动),然后就可以调用服务端定义的各种服务。
客户端是如何获取到这个Binder引用的呢?
(参考博客:http://blog.youkuaiyun.com/zcxwww/article/details/51208190)
详解见http://gityuan.com/2015/10/31/binder-prepare/中Binder系列
1.IPC原理
每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对应一个4GB的虚拟地址空间,其中3GB是用户空间,1GB是内核空间,当然内核空间的大小是可以通过参数配置调整的。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行交互。
Ioctl是设备驱动程序中对设备的I/O通道进行管理的函数
摘自:http://blog.youkuaiyun.com/u010961631/article/details/20479507
记住:Read The Fucking Source Code.
2.Binder原理
都通过与Binder驱动进行交互的
图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需自定义实现client、Server端,借助Android的基本平台架构便可以直接进行IPC通信。
3.Binder驱动概述
Binder驱动是Android专用的,但底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册,作为虚拟字符设备,没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的初始化(binder_init),打开 (binder_open),映射(binder_mmap),数据操作(binder_ioctl)。
binder_ioctl()函数负责在两个进程间收发IPC数据和IPC reply数据。
代码:
两个核心方法binder_thread_write()和binder_thread_read()方法
代码:
流程:
• 首先,把用户空间数据ubuf拷贝到内核空间bwr;
• 当bwr写缓存有数据,则执行binder_thread_write;当写失败则将bwr数据写回用户空间并退出;
• 当bwr读缓存有数据,则执行binder_thread_read;当读失败则再将bwr数据写回用户空间并退出;
• 最后,把内核数据bwr拷贝到用户空间ubuf。
小节:binder_init:初始化字符设备;
binder_open:打开驱动设备,过程需要持有binder_main_lock同步锁;
binder_mmap:申请内存空间,该过程需要持有binder_mmap_lock同步锁;
binder_ioctl:执行相应的ioctl操作,该过程需要持有binder_main_lock同步锁;
当处于binder_thread_read过程,read_buffer无数据则释放同步锁,并处于wait_event_freezable过程,等有数据到来则唤醒并尝试持有
4.Binder通信协议
Binder协议包含在IPC数据中,分为两类:
BINDER_COMMAND_PROTOCOL:binder请求码,以”BC_“开头,简称BC码,用于从IPC层传递到Binder Driver层;
BINDER_RETURN_PROTOCOL :binder响应码,以”BR_“开头,简称BR码,用于从Binder Driver层传递到IPC层;
Binder IPC通信至少是两个进程的交互:
client进程执行binder_thread_write,根据BC_XXX命令,生成相应的binder_work;
server进程执行binder_thread_read,根据binder_work.type类型,生成BR_XXX,发送到用户空间处理。
通信过程图:
5.Binder内存机制
虚拟进程地址空间(vm_area_struct)和虚拟内核地址空间(vm_struct)都映射到同一块物理内存空间。当Client端与Server端发送数据时,Client(作为数据发送端)先从自己的进程空间把IPC通信数据copy_from_user拷贝到内核空间,而Server端(作为数据接收端)与内核共享数据,不再需要拷贝数据,而是通过内存地址空间的偏移量,即可获悉内存地址,整个过程只发生一次内存拷贝。一般地做法,需要Client端进程空间拷贝到内核空间,再由内核空间拷贝到Server进程空间,会发生两次拷贝。
对于进程和内核虚拟地址映射到同一个物理内存的操作是发生在数据接收端,而数据发送端还是需要将用户态的数据复制到内核态。到此,可能有读者会好奇,为何不直接让发送端和接收端直接映射到同一个物理空间,那样就连一次复制的操作都不需要了,0次复制操作那就与Linux标准内核的共享内存的IPC机制没有区别了,对于共享内存虽然效率高,但是对于多进程的同步问题比较复杂,而管道/消息队列等IPC需要复制2两次,效率较低。这里就不先展开讨论Linux现有的各种IPC机制跟Binder的详细对比,总之Android选择Binder的基于速度和安全性的考虑。
6.ServiceManager
ServiceManager核心功能:查询和注册服务。
ServiceManger集中管理系统内的所有服务,通过权限控制进程是否有权注册服务,通过字符串名称来查找对应的Service; 由于ServiceManger进程建立跟所有向其注册服务的死亡通知, 那么当服务所在进程死亡后, 会只需告知ServiceManager. 每个Client通过查询ServiceManager可获取Server进程的情况,降低所有Client进程直接检测会导致负载过重。
ServiceManager启动流程:
1. 打开binder驱动,并调用mmap()方法分配128k的内存映射空间:binder_open();
2. 通知binder驱动使其成为守护进程:binder_become_context_manager();
3. 验证selinux权限,判断进程是否有权注册或查看指定服务;
4. 进入循环状态,等待Client端的请求:binder_loop()。
5. 注册服务的过程,根据服务名称,但同一个服务已注册,重新注册前会先移除之前的注册信息;
6. 死亡通知: 当binder所在进程死亡后,会调用binder_release方法,然后调用binder_node_release.这个过程便会发出死亡通知的回调.
ServiceManager最核心的两个功能为查询和注册服务:
• 注册服务:记录服务名和handle信息,保存到svclist列表;
• 查询服务:根据服务名查询相应的的handle信息。
7.FrameWork层
binder在framework层,采用JNI技术来调用native(C/C++)层的binder架构,从而为上层应用程序提供服务。 看过binder系列之前的文章,我们知道native层中,binder是C/S架构,分为Bn端(Server)和Bp端(Client)。
对于java层在命名与架构上非常相近,同样实现了一套IPC通信架构。