深入浅出零拷贝及应用
一、传统文件传输过程
1、Linux文件的读写流程
分享数据的过程
- 用户视角:小明通过点击分享,途经网络传输,将消息传送给小明的朋友。于是,对方即可收到消息
- 操作系统视角
//从磁盘读取文件
read(file,tmp_buf,len);
//通过网络协议发送给客户端
write(socket,tmp_buf,len);
2、Linux传统文件传输过程
//发送文件名给接收端
_, err = conn.Write([]byte(fileName))
if err != nil {
fmt.Println("conn.Write err:", err)
return
}
//读取服务器回发的信息
buf := make([]byte, 16)
n, err := conn.Read(buf)
if err != nil {
fmt.Println("conn.Read err:", err)
return
}
3、传统I/O操作中的瓶颈
内核空间为什么不能直接使用用户空间的数据呢?
- 内核不能信任任何用户空间的指针
- 稳定性与效率
4、CPU数据拷贝方式
(1)、I/O轮询
- 优点:实现简单
- 缺点:占用CPU全部资源,效率低
(2)、I/O中断
- 优点:对比IO轮询的方式,一定程度上释放了CPU资源
- 缺点:在大数据量传输的情况下,CPU会反复中断
(3)、DMA传输
- 优点:彻底减少了一次CPU拷贝
- 缺点:依赖设备硬件支持
二、零拷贝的实现方式
1、零拷贝定义:
1)、减少用户空间和内核空间之间的CPU拷贝次数 2)、减少上下文切换次数
2、介绍零拷贝的多种实现方式:
- mmap + write
- sendfile
- sendfile + gather
- splice
- 总结
三、Go语言中的实现
mmap + write的使用
map_file, err := os.Create("./input.txt")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
mmap, err := syscall.Mmap(int(map_file.Fd()),0,10,syscall.PROT_READ|syscall.PROT_WRITE, Syscall.MAP_SHARED)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
ioutil.WriteFile("./result.txt",mmap,0666)
四、零拷贝的应用
Kafka vs RocketMQ 如何通过零拷贝技术提升吞吐率与实现百万级并发
一、Kafka发送消息的过程
Kafka性能优化点
1、顺序读写:
- producer每次会追加写入到partition
- consumer每次消费的时候,根据offset进行顺序读取
- 批量刷盘
2、页缓存技术:
- 利用linux的page cache技术
- 异步落盘,减少磁盘I/O次数
- 通过Replication机制去解决数据丢失的问题
3、零拷贝mmap:
4、零拷贝sendfile:
- consumer不需要对数据进行修改,可以采用零拷贝方式
- 对比传统read() + write():节省2次cpu拷贝、2次上下文切换
二、RocketMQ发送消息的过程
1、RocketMQ优化点:mmap
- 通过mmap + write()实现,满足小数据需求
- 通过文件预热的方式来解决缺页的问题
2、RocketMQ vs Kafka
3、各种拷贝实现方式对比