零拷贝的简单复习
PageCache
PageCache是内核缓冲区
DMA
没有DMA前的IO:整个数据的传输过程,都需要 CPU 亲自参与搬运数据的过程,而且这个过程中CPU 是不能做其他事情的
CPU发起IO
磁盘将数据放到磁盘缓冲区
CPU将磁盘缓冲区数据放到内核缓冲区
CPU将内核缓冲区放到用户缓冲区
DMA技术(直接内存访问):CPU 不再参与「将数据从磁盘控制器缓冲区搬运到内核空间」的工作,这部分工作全程由 DMA 完成
CPU发起IO
磁盘将数据放到磁盘缓冲区
DMA将磁盘缓冲区数据放到内核缓冲区
DMA将内核缓冲区放到用户缓冲区
有DMA的时候,CPU全盘负责磁盘IO
有了DMA后,DMA分担了IO中磁盘文件拷贝到磁盘缓冲区和磁盘缓冲区拷贝到内核缓冲区的工作
传统文件传输
四次拷贝:流程:磁盘文件->内核缓冲区->用户缓冲区->Socket缓存区->网卡
四次用户态和内核态的上下文切换:
read():用户态变成内核态,结束后内核态变回用户态
write():用户态变成内核态,结束后内核态变回用户态
优化文件传输的性能
- 减少系统调用次数,一次系统调用必然会发生 2 次上下文切换,因为上下文开销大,所以我们要尽量减少系统调用read(),write()
- 数据实际上不需要搬到用户空间,因为在用户空间我们并不会对数据「再加工」,因此用户的缓冲区是没有必要存在的
零拷贝
传统文件拷贝流程:
DMA拷贝 CPU拷贝 CPU拷贝 DMA拷贝
磁盘文件————————>内核缓冲区————————>用户缓冲区————————>Socket缓存区————————>网卡
零拷贝实现方案:
- mmap+write 4次【上下文切换】+3次【数据拷贝】
- sendfile 2次【上下文切换】+3次【数据拷贝】
通过上面两种方法代替传统的read()+write()可以减少【上下文切换】和【数据拷贝】的次数
mmap+write调用次数:两次系统调用
mmap+write拷贝流程: 无需经过内核缓冲区
DMA拷贝 CPU拷贝 DMA拷贝
磁盘文件————————>内核缓冲区————————>Socket缓存区————————>网卡
sendfile调用次数:只需一次系统调用,它可以替代前面的 read () 和 write () 这两个系统调用,这样就可以减少一次系统调用,也就减少了 2 次上下文切换的开销
sendfile拷贝流程:无需经过内核缓冲区
DMA拷贝 CPU拷贝 DMA拷贝
磁盘文件————————>内核缓冲区————————>Socket缓存区————————>网卡
真正的零拷贝
网卡支持 SG - DMA,sendfile就能实现真正的零拷贝
全程只需要DMA参与不需要CPU
一次系统调用
2次【上下文切换】+3次【数据拷贝】
拷贝流程:磁盘文件->内核缓冲区->网卡 无需CPU参与,无需经过Socket
DMA拷贝 DMA拷贝
磁盘文件————————>内核缓冲区————————>网卡
PageCache(内核缓冲区)的作用
「内核缓冲区」实际上是磁盘高速缓存(PageCache)
DMA 把磁盘里的数据搬运到内存里,这样就可以用「读写磁盘」替换成「读写内存」
提高磁盘读写性能: PageCache 来缓存最近被访问的数据,当空间不足时淘汰最久未被访问的缓存
减少磁盘IO:
- 【预读功能】+【顺序读】
- 内核的 I/O 调度算法会缓存尽可能多的 I/O 请求在 PageCache 中,最后「合并」成一个更大的 I/O 请求再发给磁盘
PageCache的缺点
大文件的传输,不应该使用 PageCache,也就是说不应该使用零拷贝技术
缓存被大量文件数据洗刷,几乎不可能利用到PageCache 中缓存的最近被访问的数据
所以大文件传输我们要用【直接IO+异步IO】