Linux传统IPC
进程隔离
进程与进程间内存不共享,数据交互需要IPC
进程空间
进程空间分为:
- 用户空间(User Space)
- 内核空间(Kernel Space)
现代操作系统采用虚拟内存,比如32位系统,其寻址空间是2的32次方,即4GB(对于操作系统而言,一个地址=1byte)
摘自网络:虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。
以Linux而言,将最高的 1GB 字节供内核使用;较低的 3GB 字节供各进程使用(图片来自网络,侵删)
进程调用
系统调用是用户空间访问内核空间的唯一方式,即内核可控,提升了系统安全性和稳定性。 Linux中有两级保护机制:
- 0级,处理器执行内核代码(内核态)
- 3级,处理器执行用户代码(用户态)
系统调用主要通过如下两个函数来实现:
- copy_from_user() //将数据从用户空间拷贝到内核空间
- copy_to_user() //将数据从内核空间拷贝到用户空间
(图片来自网络,侵删)
- 数据存放用户空间的内存缓存中,通过系统调用,进入内核态
- 内核程序在内核空间开辟内核缓存区,调用copy_from_user() ,将数据拷贝到内核空间的缓存
- 接收方相反,调用 copy_to_user()
缺点:
- 二次拷贝,效率低
- 接收方不知道数据大小,要么开辟尽可能大空间,要么事先去获取数据大小
Binder IPC
Binder驱动
Linux 的动态内核可加载模块(Loadable Kernel Module,LKM)的机制导致Android 系统可以通过动态添加一个内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信。这个运行在内核空间,负责各个用户进程通过 Binder 实现通信的内核模块就叫 Binder 驱动(Binder Dirver)。
(图片来自网络,侵删)
内存映射mmap
Binder IPC机制中涉及到的内存映射通过 mmap() (memory map)来实现,即将用户空间的一块内存区域映射到内核空间。映射后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。正是因为mmap的一次拷贝,才会比传统Linux的IPC效率高
内核中的binder_mmap函数:申请一块物理内存A,然后在Server端的用户空间B和内核空间C同时进行映射。
物理A映射内核空间C
物理A映射用户空间B
相当于B和C相互映射
实现原理
- Binder驱动在内核空间创建数据接收缓存区;
- 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及数据接收缓存区和接收方进程的用户空间的映射关系;
- 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核缓存区,因为有这层映射关系,因此相当于把数据发送到了接收方的进程的用户空间,即
A与B映射,B与C映射,对A进行写入数据,则C也能收到数据
通信过程
(图片来自网络,侵删)
- 1.SM进程使用 BINDER_SET_CONTEXT_MGR 命令通过 Binder 驱动将自己注册成为服务管家;可以参考我之前写的ServiceManager启动流程
- 2.Server端(比如SystemServer)通过Binder驱动向ServiceManager注册Binder(即服务),Binder驱动为Binder创建结点以及引用,然后将引用+名字发给ServiceManager,写入一张查找表
(这里可能有个疑问,服务注册过程其实也是跨进程,如何拿到SM的?其实在【ServiceManager启动流程】文章已经说明,服务管家SM的引用可以在Binder驱动中直接拿到,引用为0,不管是Client端还是Service端) - 3.Client通过名字就能在ServiceManager中找到对应的引用(不过返回的是代理对象objectProxy,方法名+参数都一样)
//AMS/PMS/WMS都是实体Binder:
ActivityManagerService extends IActivityManager.Stub
PackageManagerService extends IPackageManager.Stub
WindowManagerService extends IWindowManager.Stub
abstract class Stub extends android.os.Binder
Binder驱动类似路由器,负责转发消息,ServiceManager类似DNS,通过字符串名字找到对应的代理对象返回给Client
不同角度定义Binder
- 1.进程间通信角度,Binder是一种进程间的通信方式(在Binder驱动的帮助下,代理对象和实体对象相互转换,从而可以发送数据返回结果)
- 2.Server进程角度,Binder是实体对象(比如AMS/WMS/PMS,他们都是继续Binder)
- 3.Client进程角度,Binder是Binder实体的代理对象(Binder驱动根据名字返回对应的代理对象,代理对象调用同名方法,传入相同的参数,然后Binder驱动找到对应的Binder实体,调用真正的方法,最后返回想要的结果)
借助AIDL手动编码实现Binder IPC
可以参考个人练习项目AndroidTest
结尾
失眠必读系列未完待续,有任何错误,虚心讨教,欢迎指正...
参考资料:
zhuanlan.zhihu.com/p/430557625
blog.csdn.net/Hfengxiang/…
blog.csdn.net/mcryeasy/ar…
www.cnblogs.com/huxiao-tee/…