一、进程通信中“拷贝”的概念
拷贝:
- 广义上讲就是任何数据复制,但是在Linux系统中比较特殊,这里的概念比较狭义,物理内存之间的数据复制才叫拷贝
- IO设备、Input 、DMA(Direct Memory Access)与其他设备之间的数据复制不能称为拷贝
为什么要着重强调这个概念呢?
因为很多博客中讲sendfile、mmap,多次拷贝的时候出现了描述矛盾的问题。
1、两次拷贝 (这张图上有3个物理内存)
- Copy From User 用户态->内核态
- Copy To User 内核态->用户态
因此传统IO上,进程间的通信经过两次拷贝,而且这只是单向传输数据,如果B进程要返回结果给A进程,只需要把这张图中的A进程和B进程角色关系调换一下。也是就是说,如果是带返回值的IPC,至少需要4次拷贝。
细品Copy这个单词:
我们发现,Copy From User和Copy To User ? 作为主谓宾结构,到底是Who在Copy?
答案:是内核驱动程序,说明了一件事,进程通信需要内核允许
2、一次拷贝(这张图上有2个物理内存)
Binder 通信的实质,Binder 服务端和Binder用户端,进程A和B分别和内核态建立映射关系,这个映射关系依靠mmap完成,一次拷贝。
IPC通信,最多只需要2次拷贝,比传统方式少一倍。
3、0拷贝(这张图上1个物理内存)
A,B两用户态进程和内核态建立了同一虚拟内存地址的内存,因此,实现了内存共享,同样也是通过mmap实现
问题来了,共享内存这么高校为什么Android 通信不用?
实际上,Android并没有不用,只是在用户进程之间通信不推荐试用,Android使用共享内存的。Android使用共享内存的地方是SurfaceFlinger,Android App中可以使用共享内存的是工具是MemoryFile。
Google为什么不推荐呢?
共享内存无法实现高并发的复杂控制,可能出现用户进程A修改用户进程B数据的问题,对于一对一的通信缺陷并不大,但是多对一,多对多就是致命伤?
为什么SurfaceFlinger会使用共享内存?
- SurfaceFlinger数据控制使用Binder,View绘制使用GraphicBuffer 共享内存
- 共享内存传输数据非常高校,SurfaceFlinger直接和显示设备关联,需要尽可能避免图像数据延迟导致丢帧问题
- 数据类型单一,无需安全校验
- 进程之间是一对一,通过优先级合成图像