零拷贝
用户进程拥有用户空间的缓存区;CPU操作文件时无法直接从磁盘中读取,而是从内存读取。
默认情况下读取一个文件并将文件内存发送的过程是 磁盘文件->内核态PageCache->用户缓冲区->socket缓冲区->网卡。如下图,进行了4次数据拷贝,以及4次上下文切换
之后出现了DMA技术,简单理解,DMA就是在主板上有一块独立的芯片。在进行内存和IO设备数据传输的时候,直接通过DMA搬运。不需要CPU去处理Copy
零拷贝技术是一个思想,指的是指计算机执行操作时,CPU 不需要先将数据从某处内存复制到另一个特定区域。
零拷贝技术的具体实现方式有很多,例如:
- sendfile
- mmap
- Direct I/O
- splice
不同的零拷贝技术适用于不同的应用场景 - DMA技术:DMA负责内存与其他组件之间的数据拷贝,CPU 仅需负责管理,而无需负责全程的数据拷贝;
使用 page cache 的 zero copy: - sendfile:一次代替 read/write 系统调用,通过使用 DMA 技术以及传递文件描述符,实现了 zero copy;
- mmap:仅代替 read 系统调用,将内核空间地址映射为用户空间地址,write 操作直接作用于内核空间。通过 DMA 技术以及地址映射技术,用户空间与内核空间无须数据拷贝,实现了 zero copy; 不使用 page cache 的 Direct I/O:读写操作直接在磁盘上进行,不使用 page cache 机制,通常结合用户空间的用户缓存使用。通过 DMA 技术直接与磁盘/网卡进行数据交互,实现了 zero copy;
案例:
1、Kafka接收消息并持久化时,使用 mmap 机制,能够基于顺序磁盘 I/O 提供高效的持久化能力
2、发送消息时,使用sendfile机制
使用 mmap 来对接收到的数据进行持久化,使用 sendfile 从持久化介质中读取数据然后对外发送是一对常用的组合。但是注意,你无法利用 sendfile 来持久化数据,利用 mmap 来实现 CPU 全程不参与数据搬运的数据拷贝。
总结:
DMA 技术使得内存与其他组件,例如磁盘、网卡进行数据拷贝时,CPU 仅仅需要发出控制信号,而拷贝数据的过程则由 DMAC 负责完成。 Linux 的零拷贝技术有多种实现策略,但根据策略可以分为如下几种类型:
- 减少甚至避免用户空间和内核空间之间的数据拷贝:在一些场景下,用户进程在数据传输过程中并不需要对数据进行访问和处理,那么数据在 Linux 的 Page Cache 和用户进程的缓冲区之间的传输就完全可以避免,让数据拷贝完全在内核里进行,甚至可以通过更巧妙的方式避免在内核里的数据拷贝。这一类实现一般是是通过增加新的系统调用来完成的,比如 Linux 中的 mmap(),sendfile() 以及 splice() 等。
- 绕过内核的直接 I/O:允许在用户态进程绕过内核直接和硬件进行数据传输,内核在传输过程中只负责一些管理和辅助的工作。这种方式其实和第一种有点类似,也是试图避免用户空间和内核空间之间的数据传输,只是第一种方式是把数据传输过程放在内核态完成,而这种方式则是直接绕过内核和硬件通信,效果类似但原理完全不同。
- 内核缓冲区和用户缓冲区之间的传输优化:这种方式侧重于在用户进程的缓冲区和操作系统的页缓存之间的 CPU 拷贝的优化。这种方法延续了以往那种传统的通信方式,但更灵活。
补充: 用户态和内核态
Linux内核控制了硬件资源,例如CPU协调、内存分配。用户态就是提供应用程序运行的空间,为了时应用程序访问到CPU、内存、IO等,所以内核提供了一系列的系统调用接口
系统调用主要分为2类:
- 库函数(https://zhuanlan.zhihu.com/p/344311940)
- shell