[操作系统] 减少IO拷贝的方式
参考文章:https://mp.weixin.qq.com/s/xej6klx2q0G1fp82_vKCOg
减少IO拷贝的目的是将数据直接从IO源(外存等)直接读取到目标(内存、网卡等)位置,而尽量减少在内存中的复制拷贝(通常是由于系统缓存IO机制带来的问题)。
主要思路有三种:
1. 用户态直接IO,即将数据直接IO读取到用户空间,不经过内核空间。
2. 减少拷贝次数,避免数据在缓冲区间拷贝等。
3. 写时复制技术,多个进程共享同一块数据时,不立即复制数据到新的内存块,耳式当某个进程对数据进行修改后再执行拷贝操作。
1. 用户态直接IO
在IO模式中已有详细讲述,此处不再赘述。其有点在于可以跳过内核空间,缺点在于CPU和磁盘IO有速率差距,需要配合异步IO使用。
2. mmap+write
将内核中的读缓冲区与用户空间的缓冲区直接进行映射,省去了从内核缓冲区读取数据到用户缓冲区的步骤。(注意:这样仍然会有用户态/内核态切换消耗)
这种方式通常用在处理大文件上,对于小文件时会造成内存浪费(由于内存映射需要对其页边界,最小单位为4KB,不是4KB的整数倍会导致浪费空间)。
3. SendFile
用以在讷河空间内部进行IO传输,一般针对不同外设传输数据的情况。
设想从磁盘读取数据拷贝到socket缓冲区进行发送的情景。
若是普通情况,那么数据流为:磁盘->内核缓冲区->用户缓冲区->socket缓冲区->网卡
使用SendFile会跳过用户空间:磁盘->内核缓冲区->socket缓冲区->网卡
4. SendFile + DMA gather copy
针对SendFile机制的改进,避免将数据拷贝到socket缓冲区,而是将传冲去文件描述符和数据长度拷贝过去,将socket地址映射到内核缓冲区,使得DMA直接从内核缓冲区拷贝数据到网卡。
数据流程为:磁盘->内核缓冲区->网卡
5. Splice
在讷河缓冲区和网络缓冲区间建立管道,避免执行CPU拷贝操作。其文件描述符参数中必须有一个是管道设备。
6. 写时复制(Copy-On-Write)
当多个进程共享同一块数据时,如果其中一个进程需要对这份数据进行修改,那么就需要将其拷贝到自己的进程地址空间中。
7. 缓冲区共享
所有进程维护同一个缓冲区池,该缓冲区池可被映射到用户空间或者内核空间。
补充:
RocketMQ采用了mmap+write,适用于业务及消息(小块文件)。
Kafka采用了SendFile,适用于系统日志消息(高吞吐量大块文件)。