Linux 系统提供的进程间通信(IPC)方式
- Signals 信号量(System V IPC)
- Pipes 管道
- Sockets 套接字
- Mesage Queue 消息队列(System V IPC)
- Shared Memory 共享内存(System V IPC)
Android 端为什么很少用Linux原有的 进程间通讯方式
- 拮据的内存,移动设备上的内存情况不同于PC平台,内存受限,因而需要有合适的机制来保证对空闲进程的回收
- Android 不支持System V IPCs
- 安全性问题显得更为突出,移动平台特有的权限问题
- 需要Death Notification(进程终止的通知)的支持
Android 端使用linux原有的IPC 有哪些
- 在请求Zygote fork进程的时候使用的是Socket IPC
Android 端使用binder的系统服务有哪些
Binder 相较于传统IPC来说为啥适合Android 系统
- Binder本身是C/S架构的,这一点更符合Android系统的架构
- 性能上更有优势:管道,消息队列,Socket的通讯都需要两次数据拷贝,而Binder只需要一次,在binder 驱动层(每个进程都是共享kernel层的,所以只需要1次拷贝)
- 安全性更好:传统IPC形式,无法得到对方的身份标识(UID/GID),而在使用Binder IPC时,这些身份标示是跟随调用过程而自动传递的。Server端很容易就可以知道Client端的身份,非常便于做安全检查
如何跨进程调用
那么如何使得调用者能像上述一样简单地调用远程方法?毕竟两者存在于不同的进程空间里面。那么可以引入一个黑盒模块,用这个黑盒模块来帮我们完成其中的细节,这个模块也被称为Binder Driver。方法的跨进程调用受到了 Linux 进程隔离的限制,而解决方案就是将其置于所有进程都能共享的区域 -- Kernel,而 Binder Driver 提供的功能也就是让各进程使用内核空间,将进程中的地址和Kernel中的地址映射起来,其中Linux ioctl 函数实现了从用户空间转移到内核空间的功能。在 Binder Driver 的支持下,就能实现跨进程调用。
Client / Server 架构
在设计的时候,Binder Framework 交互模型采用的是客户端/服务器模型。客户端需要调用远程服务的内容时, 会初始化一个连接,并等待服务器的返回,同时会block住自己。Binder Framework在客户端这边实现了一个代理,而在服务端,通过线程池的方式来响应请求。在如下图所示,A进程就是客户端,并且通过Proxy来完成对Binder Driver内核的交互。Process B是系统服务进程,在这个进程里面维护这多个Binder Thread,直到达到设置的线程上限。Proxy对象通过和Binder Driver进行交互,从而使得Binder Driver将信息传递到目标对象。从Android开发者的角度出发,Binder Framework提供的最方便的改进就是能像调用本地方法一样调用远程方法或对象。客户端的进程调用会在Server进程返回之前一直处于block的状况。在这种机制下,客户端就不必提供一个单独的线程模型和回调机制。(同步转异步简单,而异步变同步则很困难)
传递的数据格式
在实现跨进程调用的时候,涉及到参数和命令的传递,得有一个合适的数据结构来表达需要远程执行的东西。
Target是指目标binder,Cookie这涵盖着一些内部信息,sender Id则包含了安全相关的信息,data则包含着一些数据的数组。每个数组的Entry是由相关的命令和参数组成的,这部分参数将传递给目标binder。
而这里面的Sender Id 则非常的重要,不仅可以起到唯一标示Binder的作用,还可以在跨进程的地方作为标记的作用,在接下来的文章里再详细说明。
Server Manager
我们接触的服务很多,从Display到Location,从Audio到Wifi,如果我们和每一个服务都通过前面描述的方式进行交互,即便通过 Proxy 的方式也是非常的繁琐。而且在调用每个系统服务的时候,必须知道对应的系统服务的地址,而系统服务的地址出于安全性的考虑也不应该暴露出来。那么如何方便我们进行系统服务调用了?
Service Manager 就是来帮助我们解决这个问题。这是Binder Framework的一个特殊节点,也是第一个起点。其作为一个命令服务,起到了DNS的作用,使得可以通过名字的方式来查找相应的Binder接口。这很重要,因为客户端不应该知道远程服务的调用地址,如果知道了这势必会很不安全。每一个Binder需要将自己的名字和Binder Token交给Service Manager,客户端只需要知道服务的名字就可以。
Service Manager 怎么当DNS的?
应用程序通过Service Manager 获取其他service 的client proxy,但是怎么拿 ServiceManager呢? Service Manager 首先就被创建了,并被赋予了一个特殊的句柄,这个句柄就是 0 。换而言之,其他 Server 进程都可以通过这个 0句柄 与 Service Manager 进行通信,在整个系统启动时,其他 Server 进程都向这个 0句柄 进行注册,从而使得客户端进程在需要调用服务时,能够通过这个 Service Manager 查询到相应的服务进程
Proxy 接口
首先定义一套相同的接口,服务端 和 客户端分别使用这套接口,服务端具体实现了这套接口的相应逻辑,而客户端也实现了这套接口,不过接口里面的具体实现是调用相应的远程服务接口,将函数参数打包,通过Binder向Server发送申请并等待返回值。
做为Proxy设计模式的基础,首先定义一个抽象接口类封装Server所有功能,其中包含一系列纯虚函数留待Server和Proxy各自实现。由于这些函数需要跨进程调用,须为其一一编号,从而Server可以根据收到的编号决定调用哪个函数。而这里的 Binder 具有唯一的标示性。
参考
- [Binder] ]https://www.jianshu.com/p/c11333e77910
https://blog.csdn.net/Frakie_Kwok/article/details/70049175?locationNum=10&fps=1
https://www.jianshu.com/p/9a13f1afbc6e/
http://www.codeceo.com/article/android-binder-part-one.html
- [System V IPC] https://www.cnblogs.com/alantu2018/p/8466220.html