目录
一、概述
其他虚拟网络技术相关请查看: 虚拟网络技术资料汇总
零拷贝(Zero-Copy)技术是一个思想,是一种 I/O 操作优化技术,可以快速高效地将数据在文件系统移动和网络接口之间传输数据,而不需要将其从内核空间复制到用户空间。
但零拷贝并不代表一次数据复制都没有,而是尽最大可能的减少。
实际上Zero-Copy中有一项核心技术即DMA,在IO操作中扮演十分重要的角色,具体原理不是本文范围,可自行查阅资料。
二、传统I/O
先看一个常规的IO操作,需要从磁盘中读取数据,通过网络传输出去。
补充一个技术点
💡 DMA技术是Direct Memory Access的缩写。其意思是“存储器直接访问”。它是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。(但注意一点:同一设备间数据copy需要CPU进行?)
有了DMA技术之后,过程如下:
还是 4 次数据拷贝,其中两次是 DMA 的拷贝,另外两次则是通过 CPU 拷贝:
第一次拷贝
,把磁盘上的数据拷贝到操作系统内核的缓冲区里,这个拷贝的过程是通过 DMA 搬运的。第二次拷贝
,把内核缓冲区的数据拷贝到用户的缓冲区里,于是我们应用程序就可以使用这部分数据了,这个拷贝到过程是由 CPU 完成的。第三次拷贝
,把刚才拷贝到用户的缓冲区里的数据,再拷贝到内核的 socket 的缓冲区里,这个过程依然还是由 CPU 搬运的。第四次拷贝
,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程又是由 DMA 搬运的。
三、零拷贝技术
我把零拷贝技术分为两大类,分类依据是,是否依托OS的PageCache。
1.基于PageCache:sendfile,mmap,splice
2.脱离PageCache:Direct I/O
3.1 Sendfile技术
需要硬件支持,磁盘到socket之间直接传数据,不做数据处理。
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
sendfile技术有两个发展阶段:
1.在Linux 2.1-2.4的版本中:1次CPU copy,2次dma copy,2次上下文切换(两次系统调用)。
2.Linux 2.4版本后,sendfile + DMA gather copy技术组合实现2次dma copy,2次上下文切换。
只适用于将数据从文件拷贝到 socket 套接字上的传输过程。这种技术需要硬件和驱动的支持,
典型运用场景:kafka消费数据时,消息数据从磁盘传输到网络。
3.2 splice技术
Linux 在 2.6.17 版本引入 splice 系统调用,不仅不需要硬件支持,还实现了两个文件描述符之间的数据零拷贝。在 Linux 2.6.23 版本中, sendfile 机制的实现已经没有了,但是其 API 及相应的功能还在,只不过 API 及相应的功能是利用了 splice 机制来实现的。
与sendfile
不同的是,splice
允许任意两个文件互相连接,而并不只是文件与socket
进行数据传输。对于从一个文件描述符发送数据到socket
这种特例来说,一直都是使用sendfile
系统调用,而splice
一直以来就只是一种机制,它并不仅限于sendfile
的功能。
3.3 mmap技术
将内核空间地址映射为用户空间地址,rw直接作用内核,保留OS的缓冲能力。
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);
2次上下文切换(两次系统调用),2次DMA copy,1次CPU copy。
典型场景:Kafka生产消息落盘时,从socket直接将数据写入内核buffer。
3.4 Direct I/O技术
用户空间读取的文件直接与磁盘交互,没有中间 page cache 层。虽然文件的数据本身没有使用任何缓存,但是文件的元数据仍然需要缓存;MySQL 的 O_DIRECT 与 O_DIRECT_NO_FSYNC 配置是一个具体案例。
如何使用:
int open(const char *pathname, int flags, ... /*, mode_t mode */ );
flags指定O_DIRECT。
典型应用案例:数据库管理系统是,缓存完全自治。
四、总结
总结各类提升IO效能技术对比如下:
CPU拷贝 | DMA拷贝 | 系统调用 | 上下文切换 | |
---|---|---|---|---|
传统方法 | 2 | 2 | read/write | 4 |
mmap | 1 | 2 | mmap/write | 4 |
sendfile | 1 | 2 | sendfile | 2 |
scatter/gather copy | 0 | 2 | sendfile | 2 |
splice | 0 | 2 | splice | 2 |
direct I/O | 0 | 2 | open(O_DIRECT) |
DMA的2次copy都少不了,零拷贝带来是CPU copy的减少和上下文切换的减少。