详解零拷贝原理

前言

总所周知,磁盘读写速度令人堪忧,特别是机械硬盘,读写速度相差内存 10 倍以上,所以针对优化磁盘的技术非常的多,比如零拷贝、直接 I/O、异步 I/O 等等,下面我们就分几个点来讲讲零拷贝技术。

DMA技术是什么?

直接内存访问(Direct Memory Access) 技术,在进行 I/O 设备和内存的数据传输的时候,数据搬运的工作全部交给 DMA 控制器,而 CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务

在没有 DMA 技术前,I/O 的过程是这样的:

  • CPU发起IO指令给磁盘
  • 磁盘收到指令,把数据放入磁盘缓冲区,然后产生一个中断;
  • CPU 收到中断信号后,把磁盘缓冲区的数据读进内核缓冲区,然后再把内核缓冲区写入到用户缓冲区,而在数据传输的期间 CPU 是无法执行其他任务的。

有 DMA 技术之后,I/O 的过程是这样的:

可以看到, 整个数据传输的过程,CPU 不再参与数据搬运的工作,而是全程由 DMA 完成,大大提高了CPU的性能。

传统IO

传统文件传输,将磁盘上的文件读取出来,然后通过网络协议发送给客户端。

流程: 磁盘->内核缓冲区->用户缓存区->socket缓冲区->网卡缓冲区

分析: 4次拷贝(2次DMA,2次CPU),4次状态切换,因为发生了两次系统调用,一次是 read() ,一次是 write(),每次系统调用都得先从用户态切换到内核态,等内核完成任务后,再从内核态切换回用户态

结论: 想提高文件传输的性能,就需要减少「用户态与内核态的上下文切换」和「内存拷贝」的次数

  • 要想减少上下文切换到次数,就要减少系统调用的次数
  • 在用户空间我们并不会对数据「再加工」,所以数据实际上可以不用搬运到用户空间,因此用户的缓冲区是没有必要存在的

mmap内存映射

针对传统IO的缺陷,一个优化方案就是通过mmap系统替代read系统调用,这个函数的作用是让用户缓冲区共享内核缓冲区

流程: 硬盘->内核缓冲区=用户缓存区->socket缓冲区->网卡缓冲区

分析: 3次拷贝,4次状态切换,因为mmap系统调用也是2次状态切换

结论: 但这还不是最理想的零拷贝,因为仍然需要通过 CPU 把内核缓冲区的数据拷贝到 socket 缓冲区里,而且仍然需要 4 次上下文切换

sendFile

在 Linux 内核版本 2.1 中,提供了一个专门发送文件的系统调用函数 sendfile(),它可以替代前面的 read() 和 write() 这两个系统调用,这样就可以减少一次系统调用,也就减少了 2 次上下文切换的开销。

流程: 硬盘->内核缓冲区->socke缓冲区->网卡缓冲区

分析: 3次拷贝,2次状态切换,只有一次sendFile系统调用,2次状态切换

优化: linux2.4后续优化:硬盘->内核缓冲区->网卡缓冲区

2次拷贝,2次状态切换,源头到目标,零拷贝,全程没有通过 CPU 来搬运数据,所有的数据都是通过 DMA 来进行传输的,这就是真正意义上的零拷贝。

应用场景

事实上,Kafka 这个开源项目,就利用了「零拷贝」技术,从而大幅提升了 I/O 的吞吐率,这也是 Kafka 在处理海量数据为什么这么快的原因之一。

如果你追溯 Kafka 文件传输的代码,你会发现,最终它调用了 Java NIO 库里的 transferTo方法:

arduino 复制代码
@Overridepublic   
long transferFrom(FileChannel fileChannel, long position, long count) throws IOException {   
    return fileChannel.transferTo(position, count, socketChannel);  
}

如果 Linux 系统支持 sendfile() 系统调用,那么 transferTo() 实际上最后就会使用到 sendfile() 系统调用函数。

相关推荐
雯宝6 小时前
mnt_init 函数
linux
律宏阔12 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔12 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴12 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千12 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei3712 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei3712 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei3712 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW12 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW12 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux