开发者社区> jasmine_ben> 正文

android 进程间通信原理

简介: 前言 每个Android进程只能运行在自己拥有的虚拟地址空间,对于用户空间。不同进程之间彼此是不能共享的,而内核空间是可以共享的。Client和Server进程通信就是利用进程间可以共享内核内存空间来完成底层通信工作的,Client和Server通过ioctl等和内核空间进行交互。
+关注继续查看

前言

每个Android进程只能运行在自己拥有的虚拟地址空间,对于用户空间。不同进程之间彼此是不能共享的,而内核空间是可以共享的。Client和Server进程通信就是利用进程间可以共享内核内存空间来完成底层通信工作的,Client和Server通过ioctl等和内核空间进行交互。

img_2ce1f101ce30ad53f419410d3ac13485.png
进程通信架构

1、android的IPC和RPC

RPC指的是跨进程远程调用,强调了调用的功能,即一个进程之间调用另外一个进程的方法。

IPC指的是进程间通信,android使用Binder机制来进行进程间的通信,没有调用的功能。

Android系统的RPC = Binder进程间通信+在Binder基础上建立起来的进程间函数调用机制。

2、android系统的RPC实现

img_5de75a185a283e10232b7ad72baab75d.png
RPC架构图

Android的RPC主要包含Client、Server和ServiceManager。android中使用ServiceManager来管理所有的所有的Server。ServiceManger启动后首先告诉Binder驱动,将自己标识为ServiceManager。在创建一个Server后,首先通过addService将自己交给ServiceMager管理,Client在需要调用Server时直接通过getService到ServiceManager中查找对应的Server,然后调用Server的方法。

下图给出binder在android中的整体架构,从framework到native再到kernel:

img_5ab31809f97999361686995411b41178.png
Binder整体架构


图中红色部分代表整个framework层binder架构的相关组件,Binder类代码Server端,BinderProxy代表客户端。蓝色代码Native层的Binder架构组件。上层framewoek的binder逻辑建立在native层架构的基础之上,核心逻辑都是交给native处理的。Framework的ServiceManager与native的ServiceManager并不完全对应,framework层的ServiceManager类的实现最终是通过BinderProcy传递给native层来完成的。

Server启动后会开启一个线程不停的读取Binder驱动的读接口,这是一个阻塞调用;在需要响应客户端的时候,会调用Binder驱动的写接口进行数据返回。

Client启动后会不停读取Binder驱动的读接口并阻塞;在调用Service时会开启线程调用Binder的写接口;服务器端处理完后调用写接口、唤醒阻塞中的客户端。

所有的通信都是通过底层的Binder驱动实现的。

3、RPC机制java层代码分析

img_f07f84dbf152ee95cedbceabe8045dc4.png
类图


一般我们使用如下方式来获取系统的Service,例如AlarmManager:

AlarmManager wm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);该代码的运行时序如下所示:

img_650b9c6f3ce88ed796e87d05724ec155.png
getSystemService时序

客户端通过Context.getSystemService获取远程服务时,会转到ContextImpl中调用,ContextImpl有一个ServiceFetcher内部类,通过名字知道该类用于获取Service;ContextImpl里面有个static的WALLPAPER_FETCHER,在APK启动时会加载里面的函数,如下所示,其中registerService会将每个Service对应的构造器ServiceFetcher放入SYSTEM_SERVICE_MAP中。

img_7b4460a8643292cd891bd57740aa84a7.png
创建ServiceFetcher

在调用ContextImpl.getSystemService()时,会调用SYSTEM_SERVICE_MAP对应ServiceFetcher的getService()方法,如下所示。ContextImpl有个全局mServiceCache用于缓存用户创建的Service缓存,这样用户再次获取的时候可以直接从缓存取出,避免再次创建。

img_00949662e356ac0642909a6df5ba69ce.png
SeviceFetcher.getService

如果mServiceCache没有需要的Service缓存,则调用ServiceFetcher的createService进行创建,这里就开始和ServiceManager打交道了,以AlarmManager为例,首先调用ServiceManager.getService()获取IBinder,然后调用IAlarmManager.stub.asInterface将该binder转化成客户端可以直接调用的接口,最后将该接口封装成AlarmManager给客户端使用。

img_4f595c34fe17ef95fd61a66c85037a94.png
registerService

我们看到这里调用了ServiceManagerNative.asInterface获取应IServiceManager实例,传入了BinderInternal.getContextObject(),如下所示:

img_66cbb83c8f24c8cb810d447706129b55.png
BinderInternal.getContextObject

getContextObject方法是一个JNI方法,其实sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());就相当于:sServiceManager = ServiceManagerNative.asInterface(new BinderProxy());

接下来就是调用ServiceManagerNative的asInterface函数了.

img_d67e9ad27c7b48dcb3bf9427d722aa2d.png
ServiceManagerNative.asInterface()

这里的参数obj是一个BinderProxy对象,ServiceManagerProxy提供了addService、getService的实现,也就是ServiceManager最终关联到了ServiceManagerProxy上。到这里,在java层我们已经拥有了ServiceManager的远程接口ServiceManagerProxy,对ServiceManager的所有操作将转接到ServiceManagerProxy中。

如此就实现了对android系统的ServiceManager的RPC调用。那么android系统中提供的那些Service是怎样添加到ServiceManager中去的呢?答案就在SystemServer.java类中,SystemServer伴随系统一起启动,之后会运行ServerThread线程。

img_00b6cd3ecd957400e40457318b47fdd0.png
SystemServer.init2()

ServerThread的run方法会完成所有系统Service的创建,并添加到ServiceManager中去,如下所示:

img_d07f9cb87137d97766bd432937492666.png
添加所有的系统Service

其中addService也是调用的ServiceManagerProxy的addService,这样在系统启动后,相当于在OS层维护了一群系统Service的list。

4、几个概念

IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象(Server端)以及Binder代理对象(Client获取的Proxy)的转换。

IInterface代表的就是远程server对象具有什么能力, 表示client与server端的调用契约。具体来说,就是aidl里面的接口。

Java层的Binder类,代表的其实就是Binder本地对象。BinderProxy类是Binder类的一个内部类,它代表远程进程的Binder对象的本地代理;这两个类都继承自IBinder,因而都具有跨进程传输的能力;实际上,在跨越进程的时候,Binder驱动会自动完成这两个对象的转换。

在使用AIDL的时候,编译工具会给我们生成一个Stub的静态内部类;这个类继承了Binder,说明它是一个Binder本地对象,它实现了IInterface接口,表明它具有远程Server承诺给Client的能力;Stub是一个抽象类,具体的IInterface的相关实现需要我们手动完成,这里使用了策略模式。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
由Monkey测试引发的跨多个进程的Android系统死锁问题分析
一、问题现象 1、界面定住,没有任何刷新,所有输入事件无效,包括power key 2、adb shell可以连接并操作手机 3、手机的data和sdcard存储空间已满 4、watc...
2434 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
23580 0
Android 进程生命周期 Process Lifecycle
  进程的生命周期   Android系统会尽力保持应用的进程,但是有时为了给新的进程和更重要的进程回收一些内存空间,它会移除一些旧的进程。
793 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20693 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,大概有三种登录方式:
13572 0
+关注
jasmine_ben
2013年毕业于后一直从事android相关的开发。熟悉framework和app开发的相关知识,业余时间喜欢做些开源(https://github.com/JasmineBen)和写一些文章(https://www.jianshu.com/u/45db8e5d0d30)
33
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载