一、DMA
DMA(直接内存存取)
为什么要有 DMA 技术?
没有DMA的时候,IO整个数据的传输过程,都要需要 CPU 亲⾃参与搬运数据的过程,⽽且这个过程,CPU 是不能做其他事情的。
有了DMA技术。在进⾏ I/O 设备和内存的数据传输的时候,数据搬运的⼯作全部交给 DMA 控制器,⽽ CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。
说白了就是引入了DMA,帮CPU干了一部分工作,这样CPU就可以腾出手干其他事情了
二、传统文件IO
传统文件IO需要用到2个函数:
read(file, tmp_buf, len); write(socket, tmp_buf, len);
发⽣了 4 次⽤户态与内核态的上下⽂切换, 4 次数据拷⻉(两次DMA 拷⻉,两次 CPU 拷⻉)
传统的IO性能是非常差的,所以,要想提⾼⽂件传输的性能,就需要减少「⽤户态与内核态的上下⽂切换」和「内存拷⻉」的次数。
三、如何实现零拷⻉?
零拷⻉技术实现的⽅式通常有 2 种:
mmap + write
sendfile
mmap + write
我们可以⽤ mmap() 替换 read() 系统调⽤函数。
buf = mmap(file, len); write(sockfd, buf, len);
仍然需要 4 次上下⽂切换,因为系统调⽤还是 2 次。但是拷贝从4次变成了3次
sendfile
在 Linux 内核版本 2.1 中,提供了⼀个专⻔发送⽂件的系统调⽤函数 sendfile()
它可以替代前⾯的 read() 和 write() 这两个系统调⽤,这样就可以减少⼀次系统调⽤,也就减少了 2 次上下⽂切换的开销。
这样就只有 2 次上下⽂切换,和 3 次数据拷⻉
但是这还不是真正的零拷⻉技术,如果⽹卡⽀持 SG-DMA(The Scatter-Gather DirectMemory Access)技术(和普通的 DMA 有所不同),我们可以进⼀步减少通过 CPU 把内核缓冲区⾥的数据拷⻉到 socket 缓冲区的过程。
Linux运行这条命令查看网卡是否支持 SG-DMA
$ ethtool -k eth0 | grep scatter-gather scatter-gather: on
这样,最终只有2次上下文切换和2 次数据拷⻉
四、PageCache
零拷⻉技术是基于 PageCache 的
零拷贝第⼀步都是先需要先把磁盘⽂件数据拷⻉「内核缓冲区」⾥,这个「内核缓冲区」实际上是磁盘⾼速缓存(PageCache)
通常,刚被访问的数据在短时间内再次被访问的概率很⾼,于是我们可以⽤ PageCache 来缓存最近被访问的数据,所以,读磁盘数据的时候,优先在 PageCache 找。
但是针对⼤⽂件的传输,不应该使⽤ PageCache,也就是说不应该使⽤零拷⻉技术,因为可能由于 PageCache 被⼤⽂件占据,由于⼤⽂件难以命中 PageCache 缓存,⽽导致「热点」⼩⽂件⽆法利⽤到 PageCache,这样在⾼并发的环境下,会带来严重的性能问题。
传输⼤⽂件的时候,使⽤「异步 I/O + 直接 I/O」,因为可以绕过PageCache;
传输⼩⽂件的时候,则使⽤「零拷⻉技术」;
在 nginx 中,我们可以⽤如下配置,来根据⽂件的⼤⼩来使⽤不同的⽅式:
location /video/ { sendfile on; aio on; directio 1024m; }
当⽂件⼤⼩⼤于 directio 值后,使⽤「异步 I/O + 直接 I/O」,否则使⽤「零拷⻉技术」。