按关键词阅读:
mmap()系统调用函数会直接把内核缓冲区里的数据「映射」到用户空间 , 这样 , 操作系统内核与用户空间就不需要再进行任何的数据拷贝操作 。
本文图片
具体过程如下:
应用进程调用了mmap()后 , DMA 会把磁盘的数据拷贝到内核的缓冲区里 。 接着 , 应用进程跟操作系统内核「共享」这个缓冲区;
应用进程再调用write() , 操作系统直接将内核缓冲区的数据拷贝到 socket 缓冲区中 , 这一切都发生在内核态 , 由 CPU 来搬运数据;
最后 , 把内核的 socket 缓冲区里的数据 , 拷贝到网卡的缓冲区里 , 这个过程是由 DMA 搬运的 。
我们可以得知 , 通过使用mmap()来代替read() ,可以减少一次数据拷贝的过程 。
但这还不是最理想的零拷贝 , 因为仍然需要通过 CPU 把内核缓冲区的数据拷贝到 socket 缓冲区里 , 而且仍然需要 4 次上下文切换 , 因为系统调用还是 2 次 。
sendfile
在 Linux 内核版本 2.1 中 , 提供了一个专门发送文件的系统调用函数sendfile() , 函数形式如下:
它的前两个参数分别是目的端和源端的文件描述符 , 后面两个参数是源端的偏移量和复制数据的长度 , 返回值是实际复制数据的长度 。
首先 , 它可以替代前面的read()和write()这两个系统调用 , 这样就可以减少一次系统调用 , 也就减少了 2 次上下文切换的开销 。
其次 , 该系统调用 , 可以直接把内核缓冲区里的数据拷贝到 socket 缓冲区里 , 不再拷贝到用户态 , 这样就只有 2 次上下文切换 , 和 3 次数据拷贝 。 如下图:
本文图片
但是这还不是真正的零拷贝技术 , 如果网卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技术(和普通的 DMA 有所不同) , 我们可以进一步减少通过 CPU 把内核缓冲区里的数据拷贝到 socket 缓冲区的过程 。
你可以在你的 Linux 系统通过下面这个命令 , 查看网卡是否支持 scatter-gather 特性:
于是 , 从 Linux 内核2.4版本开始起 , 对于支持网卡支持 SG-DMA 技术的情况下 , sendfile()系统调用的过程发生了点变化 , 具体过程如下:
第一步 , 通过 DMA 将磁盘上的数据拷贝到内核缓冲区里;
第二步 , 缓冲区描述符和数据长度传到 socket 缓冲区 , 这样网卡的 SG-DMA 控制器就可以直接将内核缓存中的数据拷贝到网卡的缓冲区里 , 此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中 , 这样就减少了一次数据拷贝;
所以 , 这个过程之中 , 只进行了 2 次数据拷贝 , 如下图:
本文图片
这就是所谓的零拷贝(Zero-copy)技术 , 因为我们没有在内存层面去拷贝数据 , 也就是说全程没有通过 CPU 来搬运数据 , 所有的数据都是通过 DMA 来进行传输的 。
零拷贝技术的文件传输方式相比传统文件传输的方式 , 减少了 2 次上下文切换和数据拷贝次数 , 只需要 2 次上下文切换和数据拷贝次数 , 就可以完成文件的传输 , 而且 2 次的数据拷贝过程 , 都不需要通过 CPU , 2 次都是由 DMA 来搬运 。
所以 , 总体来看 , 零拷贝技术可以把文件传输的性能提高至少一倍以上 。
使用零拷贝技术的项目
事实上 , Kafka 这个开源项目 , 就利用了「零拷贝」技术 , 从而大幅提升了 I/O 的吞吐率 , 这也是 Kafka 在处理海量数据为什么这么快的原因之一 。
稿源:(CSDN)
【】网址:http://www.shadafang.com/c/hn10049531C2020.html
标题:coding|原来 8 张图,就可以搞懂“零拷贝”了!( 三 )