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的相关实现需要我们手动完成,这里使用了策略模式。

目录
相关文章
|
2月前
|
Android开发 移动开发 小程序
binder机制原理面试,安卓app开发教程
binder机制原理面试,安卓app开发教程
binder机制原理面试,安卓app开发教程
|
2月前
|
安全
【进程通信】信号的捕捉原理&&用户态与内核态的区别
【进程通信】信号的捕捉原理&&用户态与内核态的区别
|
2月前
|
存储
【进程信号】信号阻塞的原理
【进程信号】信号阻塞的原理
|
9天前
|
SQL 安全 Java
Android经典面试题之Kotlin中object关键字实现的是什么类型的单例模式?原理是什么?怎么实现双重检验锁单例模式?
Kotlin 单例模式概览 在 Kotlin 中,`object` 关键字轻松实现单例,提供线程安全的“饿汉式”单例。例如: 要延迟初始化,可使用 `companion object` 和 `lazy` 委托: 对于参数化的线程安全单例,结合 `@Volatile` 和 `synchronized`
19 6
|
1月前
|
安全 网络协议 算法
Android网络基础面试题之HTTPS的工作流程和原理
HTTPS简述 HTTPS基于TCP 443端口,通过CA证书确保服务器身份,使用DH算法协商对称密钥进行加密通信。流程包括TCP握手、证书验证(公钥解密,哈希对比)和数据加密传输(随机数加密,预主密钥,对称加密)。特点是安全但慢,易受特定攻击,且依赖可信的CA。每次请求可能复用Session ID以减少握手。
28 2
|
1月前
|
算法 Linux 调度
Linux进程——进程的创建(fork的原理)
Linux进程——进程的创建(fork的原理)
19 2
|
1月前
|
大数据 Linux Android开发
Android ParcelFileDescriptor实现进程间通信
Android ParcelFileDescriptor实现进程间通信
28 0
|
2月前
|
Android开发 异构计算 前端开发
Android显示原理,安卓自定义view面试
Android显示原理,安卓自定义view面试
|
2月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
|
2月前
|
Linux Shell 程序员
【进程控制】进程程序替换的原理以及exec函数族
【进程控制】进程程序替换的原理以及exec函数族

相关实验场景

更多