一、Linux跨进程通信简介
目前进程间主要通信方式有:
管道
shell命令中通过|分隔两个命令,这会将前后两个进程的输入输出用一个管道相连,以达到进程间通信的目的
ls -l /etc/ | wc -l183
本质上是一个文件,前面的进程以写方式打开文件,后面的进程以读方式打开。这样前面写完后面读,于是就实现了通信,但是管道本身并不占用磁盘或者其他外部存储的空间。它占用的是内核内存空间,由用户区空间写入和读取。所以,Linux上的管道就是一个操作方式为文件的内存缓冲区
- 匿名管道,|的形式,特点是只能在父子进程中使用,不灵活
- 命名管道,手动创建一个管道类似创建一个文件,一个进程写,一个进程读,会阻塞
- 2次数据拷贝
消息队列
MQ 采用双向链表来实现消息队列,该链表是由系统内核维护,每个 MQ 用消息队列描述符(消息队列 ID:qid)来区分,是唯一的;提供一种进程间发送数据块的方法。每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构,避免命名管道的同步和阻塞问题;在进行进程间通信时,一个进程将消息加到 MQ 尾端,另一个进程从消息队列中取消息
- 2次数据拷贝
信号
发送信号给进程,多用于消息传递和通知,不适合信息交换
信号量
常用于一种资源锁机制
信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有,也就是说信号量是用来调协进程对共享资源的访问的,并不用于进程间通信数据
共享内存
映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式
- 效率非常高,当做自己的内存区域,直接内存映射的方式,适合大数据量
- 共享内存并未提供同步机制,通常需要用其他的机制来同步对共享内存的访问,比如信号量,控制较为复杂
- 0次数据拷贝
Socket
多用于不同机器跨网络的通信
- 性能较差,需要包装数据和解包装,有较大开销
- 2次数据拷贝
二、Binder简介
Binder优势
-
性能方面的考虑,只需要1次数据拷贝,统一收口在Binder驱动
- 稳定性,基于C/S架构,Client端有什么需求,直接发送给Server端去完成,架构清晰明朗,Server端与Client端相对独立,稳定性较好,大大优于共享内存
- 安全性,传统IPC没有任何安全措施,完全依赖上层协议来确保,而这个是开放的,Client可以伪造用户信息。可靠的身份标记只应该由IPC机制本身在内核中添加,为每个App分配UID,来确定是哪个app
- 语言的角度,Linux是基于C语言,Binder对象是一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍布于系统的各个进程之中,操作这个对象就像操作本地对象一样方便
Binder通信模型
- Client
Binder代理,位于客户端进程 - Server
Binder实体,位于服务端进程 - ServiceManager (DNS服务器)
查找表,独立进程 - BinderDriver (路由器)
运行在内核空间,数据传输的核心通道,负责进程之间 Binder 通信的建立,引用计数等等
ServiceManager
- 是独立的进程,由linux的祖先init进程启动
- ServiceManager是一个守护进程,负责管理所有的 Android 系统服务,内部维护一个Map来记录已经注册的所有的service
- ServiceManager的作用是将字符形式的Binder名字转化成对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用
- 启动后打开Binder驱动
Binder驱动
- 实际上和硬件设备没有任何关系,只是实现方式和设备驱动程序是一样的
- 内存物理映射
- 驱动负责进程之间Binder通信的建立
- 使用Binder进程间通信时,所有的通信都要通过Binder驱动
实名Binder和匿名Binder
- 直接注册到ServiceManager的是实名Binder
- 通过一个实名Binder载体传递过来的Binder是匿名Binder
Binder通信整体流程
- Server创建自己的Binder,通过Binder驱动把Binder对象和对应的名字,都注册到ServiceManager
- Client发起请求,通过0号引用,先从Binder驱动中获取到ServiceManager。然后从ServiceManager里取ServerBinder,并不会给Client进程返回一个真正的Binder对象,而是返回一个看起来跟Binder一模一样的代理对象BinderProxy
- BinderProxy没有实际要用的add方法,只提供一个和Binder驱动交流的入口,它唯一能做的事情就是把参数和任务打包然后交给驱动,然后阻塞等待结果
- 驱动收到这个消息,通知Server进程,在Binder线程池调用Binder对象的add方法,然后把结果交给驱动,最后驱动把结果返回给Client进程,阻塞结束
Binder通信的代理模式
ICompute.Stub.asInterface(iBinder)
- 对于Binder的访问,如果是在同一个进程,那么直接返回原始的Binder实体,Stub
- 如果在不同进程,那么就给他一个代理对象,Stub.Proxy
-
IBinder
代表一种跨进程的能力,Binder和BinderProxy都实现了这个接口
-
Binder
创建在Server端,代表的其实就是Binder本地对象
-
BinderProxy
Binder本地对象的代理,由Client端引用并使用
唯一的功能就是通过transact把命令(方法名、参数、返回值)传递给驱动
-
Stub
继承了Binder,是真正的服务端Binder实体
实现了ICompute接口,有定义的接口能力
-
Stub.Proxy
是个壳子,代理了Binder代理 BinderProxy
实现了ICompute接口,有定义的接口能力,把命令打包传给BinderProxy,发到Binder驱动
-
IInterface
提供了 ICompute 与最原始的 IBinder 互相转换的能力,asBinder()
Stub 和 Stub.Proxy 都实现了这个接口
Stub返回Binder自己,Stub.Proxy返回BinderProxy,即都是返回真实IBinder对象
三、ActivityManagerService简介
哈哈,没有很熟,将就看下流程图