首先说结论,零拷贝技术是针对与磁盘中数据到网卡发送数据的一个简化,是系统函数级别的优化,与程序编程无关,在使用中我们只需要调用即可。
系统拷贝 read/write
前置概念:
- 页缓存(内核缓冲区):该区域是为了提升磁盘数据的读写速度所设立的一个区域,处于(主存)内存当中。
- socket缓冲区:是内核为每个 Socket 分配的内存区域,用于临时存储发送和接收的数据,处于(主存)内存当中。
os系统的加载,学过操作系统的都知道,内存当中的区域分为内核态和用户态。
在操作系统启动时,cpu会根据cs:ip表示的地址进行寻址,找到位于磁盘当中的操作系统程序部分,最终结果可以认为将系统代码搬进了ram主存当中,并且设置了一些列安全手段,除此之外则还为用户态提供了一系列系统函数。
磁盘== “外设”:
对于cpu来说,磁盘相当于外设,它会通过外设上的寄存器来管理,此处衍生出的技术为dma。
- dma:为了避免cpu的重复性的移动地址等的操作,进行的一项优化,具体来说,读取数据到缓冲区等重复性劳动可以交给dma设备。
但是dma拷贝也并非万能,在某些场景下它不能使用
- 用户空间和内核空间之间的数据拷贝:内核不能直接访问用户态内存
- 调试和诊断:dma是硬件自动完成,难以插入调试逻辑。
系统函数
mmap+write
将page buffer和程序工作缓存空间进行映射,减少page-buffer->程序缓存的cpu拷贝,但是对于程序工作缓存-》socket buffer这一部分仍然需要cpu拷贝。
为什么仍然是cpu拷贝?
这里将其理解为,我们仅仅是做到了在用户态可以看到page buffer,但是并没有打破用户态和内核态的内存空间限制,所以此处的拷贝仍然是cpu拷贝,而并非dma拷贝。
sendfile
如图,sendfile使page buff直接拷贝到socket buffer。
为什么使用的使cpu拷贝,而非DMA拷贝?
ds给我的答案使,DMA通常用于外设和主存之间,
网卡支持SG-DMA技术的sendfile
如图,该函数实际上是外设功能的调用,原本socket buffer的功能被网卡认为多余,获取可以被替代之类的,提出直接从page buffer中拷贝。
总结
实际应用:
普通的文件读写:read/write,有利于提高系统速度,但这是非具体场景的随机读取。
rocketmq: mmap+write,适合小文件并行,因为该区域用户缓存都可以看到。
kafka:sendfile,适合大文件,或者说对于小文件并行支持不好,因为为了安全性,通常会对page buffer进行锁定,避免并发问题。
ps:上述总结可以喂给ds,会得到更加优质的回答!