深度好文
深入剖析Linux IO原理和几种零拷贝机制的实现 - 知乎
10.零拷贝原理MQCloud的专栏-CSDN博客零拷贝原理
三种IO方式【中断、DMA】
轮询
中断
- 用户进程发起数据读取请求
- 系统调度为该进程分配cpu
- cpu向io控制器(ide,scsi)发送io请求
- 用户进程等待io完成,让出cpu
- 系统调度cpu执行其他任务
- 数据写入至io控制器的缓冲寄存器
- 缓冲寄存器满了向cpu发出中断信号
cpu读取数据至内存【先拷贝到内核缓冲区,再拷贝到用户缓冲区】
DMA
用户进程发起数据读取请求
- 系统调度为该进程分配cpu
- cpu向DMA发送io请求
- 用户进程等待io完成,让出cpu
- 系统调度cpu执行其他任务
- 数据写入至io控制器的缓冲寄存器
- DMA不断获取缓冲寄存器中的数据(需要cpu时钟)
- 传输至内存(需要cpu时钟)
- 所需的全部数据获取完毕后向cpu发出中断信号
- DMA负责从磁盘缓冲区拷贝到内核缓冲区。
- cpu负责内核缓冲区拷贝到用户缓冲区。
零拷贝
三种方式
3 个实现思路:
直接IO
用户态直接 I/O 只能适用于不需要内核缓冲区处理的应用程序
避免内核空间到用户空间的拷贝
1. 使用 mmap + write 代替原来的 read + write 方式
- mmap 主要的用处是提高 I/O 性能,特别是针对大文件
- mmap映射的是内核缓冲区到用户缓冲区
- mmap 的拷贝减少了 1 次拷贝【减少两次从内核空间拷贝到用户空间,增加一个从内核空间内部的拷贝】
-
2. sendFile【旧】
sendfile 只适用于将数据从文件拷贝到 socket 套接字上
sendfile 存在的问题是用户程序不能对数据进行修改,而只是单纯地完成了一次数据传输过程。3. sendfile + DMA gather copy【linux 2.4】
Linux 2.4 版本的内核对 sendfile 系统调用进行修改,为 DMA 拷贝引入了 gather 操作。sendfile 拷贝方式不再从内核缓冲区的数据拷贝到 socket 缓冲区
4. splice
可以用于任意两个文件描述符中传输数据,但是它的两个文件描述符参数中有一个必须是管道设备。写时复制
零拷贝对比
mmap:减少两次从内核空间拷贝到用户空间,增加一个从内核空间内部的拷贝
- sendfile:只发起一次系统调用(2次上下文切换)
- sendfile +DMA:不用内核空间内部拷贝了
Java中的零拷贝
- MappedByteBuffer 是 NIO 基于内存映射(mmap)这种零拷贝方式的提供的一种实现,它继承自 ByteBuffer。FileChannel 定义了一个 map() 方法,它可以把一个文件从 position 位置开始的 size 大小的区域映射为内存映像文件。
- DirectByteBuffer 是 MappedByteBuffer 的具体实现类
- FileChannel.transferTo() 底层就是sendfile() 方法。
MQ的零拷贝
RocketMQ 选择了 mmap + write 这种零拷贝方式,适用于业务级消息这种小块文件的数据持久化和传输;而 Kafka 采用的是 sendfile 这种零拷贝方式,适用于系统日志消息这种高吞吐量的大块文件的数据持久化和传输。但是值得注意的一点是,Kafka 的索引文件使用的是 mmap + write 方式,数据文件使用的是 sendfile 方式。