问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

彻底理解零拷贝技术,zero-copy

创作时间:
作者:
@小白创作中心

彻底理解零拷贝技术,zero-copy

引用
CSDN
1.
https://blog.csdn.net/weixin_43539320/article/details/145735348

在当今的互联网应用中,IO性能优化是一个永恒的话题。传统的IO操作往往伴随着多次数据拷贝,这不仅消耗CPU资源,还增加了延迟。本文将深入探讨零拷贝技术(zero-copy),从操作系统的工作原理出发,详细解析各种零拷贝技术的实现方式,帮助你理解如何在不牺牲安全性和稳定性的前提下,大幅提升IO密集型应用的性能。

为什么IO接口要基于数据拷贝?

操作系统本质上就是一个管家,目的是更加公平合理地给各个进程分配硬件资源。在操作系统出现之前,程序员需要直面各类硬件:

在这一时期,程序员掌控全局,但这也带来了掌控所有细节的负担,不利于生产力的释放。操作系统应运而生,计算机系统结构变为:

现在应用程序不需要和硬件直接交互,仅从IO的角度看,操作系统变成了一个类似路由器的角色,负责数据的分发。数据传递是通过buffer实现的,buffer是一块可用的内存空间,用来暂存数据:

操作系统作为中间商导致的问题是:你需要首先把东西交给操作系统,操作系统再转手交给硬件,这就必然涉及到数据拷贝。这就是为什么传统的IO操作必然需要进行数据拷贝的原因所在。

网络服务器案例分析

以网络服务器为例,当浏览器请求网页时,服务器需要从磁盘中读取文件并通过网络发送出去。代码示例如下:

read(fileDesc, buf, len);
write(socket, buf, len);

这两行代码看似简单,但实际上在底层发生了复杂的操作:

具体过程包括:

  1. read函数涉及一次用户态到内核态的切换,操作系统向磁盘发起IO请求,数据通过DMA技术拷贝到内核buffer。
  2. 操作系统将内核buffer中的数据拷贝到用户态buffer,read函数返回,完成第一次数据拷贝。
  3. send函数导致第二次用户态到内核态的切换,数据从用户态buffer拷贝到网络协议子系统的buffer。
  4. send函数返回,数据可能依然停留在内核中,最后通过DMA技术从socket buffer拷贝给网卡,完成发送。

发现问题与优化思路

观察上述过程,可以发现用户态并没有对数据进行任何修改,那么为什么不能直接在内核态从磁盘发送到网卡呢?这种优化思路就是所谓的零拷贝技术(Zero Copy)。

总体上来看,优化数据拷贝有三个方向:

  1. 数据无需用户态感知,完全在内核态完成数据传输。
  2. 用户态程序绕过内核直接与硬件交互。
  3. 优化用户态与内核态的数据交互方式。

mmap技术

对于网络服务器,可以使用mmap替换read/write:

buf = mmap(file, len);
write(socket, buf, len);

mmap将文件内容映射到进程地址空间,避免了从内核态到用户态的数据拷贝。但是维护文件与地址空间的映射关系是有代价的,需要正确处理异常情况。

sendfile系统调用

Linux系统专门设计了sendfile系统调用来解决数据拷贝问题:

#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

使用sendfile可以节省两次数据拷贝,因为数据无需传输到用户态:

sendfile与DMA Gather Copy

传统的DMA机制必须从一段连续的空间中传输数据:

DMA Gather Copy允许从多个源头收集数据:

这进一步提升了程序性能:

splice系统调用

splice系统调用通过管道实现数据在内核态的传输:

splice和sendfile很像,实际上sendfile后来也是基于splice实现的。保留sendfile是为了保证兼容性。

总结

高效IO的秘诀在于尽量减少CPU参与。零拷贝技术通过多种方式实现了这一目标,包括软件层面的mmap、sendfile、splice,以及硬件层面的DMA Gather Copy。这些技术在现代高性能系统中得到了广泛应用,如Kafka等消息中间件。

本文原文来自CSDN。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号