是什么

什么是零拷贝呢? 这个词想必听过不止一次了吧, 但一直没有认真的研究一下这到底是个什么玩意.

在很久之前, 一次IO 操作的流程大致是这样的:

read
read

在上面的数据获取的过程中, 发生了3次数据的拷贝, 其中2次是 CPU 全程参与的. 而这个过程, CPU 忙于拷贝数据, 无暇做其他工作.

DMA
DMA
readDMADMADMADMAread

于是, 一次数据读取的流程变成了这样:

现在, 每一次的数据读取, 都会发生2次数据拷贝(IO设备内部的就不算在其中了). 而零拷贝就是为了解决这个问题.

解决方案

mmap

想一下, 为什么数据需要从内核缓冲区拷贝到应用缓冲区? 他们用的明明是同一个物理内存呀. 还不是因为虚拟内存的存在, 所以他们在内存空间的不同地址. 如果能够让他俩共用用一段物理内存, 不就不需要拷贝数据了.

mmap
Gommap
package main

import (
	"fmt"
	"golang.org/x/exp/mmap"
)

func main() {
	at, _ := mmap.Open("./tmp.txt")
	buff := make([]byte, 1024)
	_, _ = at.ReadAt(buff, 0)
	_ = at.Close()
	fmt.Println(string(buff))
}
write
mmapDMSDMS
mmap

sendfile

我们知道, 应用程序调用系统是需要进行上下文切换的, 是否有一个函数直接告诉 CPU 把2个 IO 设备的数据进行拷贝? 这样就可以减少一次系统调用嘛.

sendfile
Gosyscall.Sendfile

你以为这就完了么? 不, 这还不是零拷贝, 这此种仍然存在一次 CPU 主导的内存拷贝.

零拷贝

mmap
DMA
ethtool -k eth0 | grep scatter-gather
CPU
sendfile
总结
kafka

如何, 简单看下来, 零拷贝也没有那么什么嘛. 此项暂时搁置, 再见