首页 > 其他分享 >零拷贝技术学习

零拷贝技术学习

时间:2024-10-31 09:33:24浏览次数:1  
标签:PageCache 技术 学习 内核 磁盘 拷贝 数据 CPU

0、引言

在现代网络系统中,随着数据传输量的不断增加,如何高效地处理网络请求成为了一个重要的研究课题。本文将从操作系统底层数据传输的过程出发,探讨零拷贝技术的原理以及它如何优化数据传输效率。这对今后学习多种相关技术将有助于我们理解其根本原理。

1、传统的磁盘I/O原理

在最初的操作系统设计中,系统内部的I/O过程涉及到以下主要步骤:

  • 用户进程调用系统函数read()方法,向操作系统请求读取数据,然后进程被挂起进行阻塞等待,此时系统从用户态转入到内核态
  • CPU收到请求后,紧接着向磁盘发起I/O请求,接着返回
  • 磁盘收到I/O请求后,会将磁盘中的数据放到磁盘内部的缓冲区中,然后向CPU发出一个IO中断信号
  • CPU收到中断信号后,开始亲自参与到数据转移的过程中,将磁盘控制器缓冲区的数据拷贝到内存的PageCache中
  • 接着CPU又从PageCache中拷贝数据到用户缓冲区中
  • 完成拷贝之后,CPU会进行返回,通知进程数据准备完毕,此时系统从内核态转入到用户态

可以看到在整个数据的传输过程中,CPU要进行数据从磁盘到内存的搬运操作,在这个过程中,CPU不能进行其他的事情。这样子做带来的后果就是,当需要搬运的数据很大的时候,就需要大量的占用CPU的时间,此时操作系统的性能就会变得很差。

那么,有没有什么办法能让CPU不去参与搬运数据的操作呢?这样子就能让CPU去处理更多重要的事务了。科学家就基于该角度,提出了DMA技术,也就是直接内存访问(Direct Memory Access)技术。

1.1、什么是pagecache?

在上面的磁盘I/O流程我们可以看到,第一次的数据拷贝是将数据从磁盘控制器拷贝到内核缓冲区中,这个内核缓冲区就是磁盘高速缓存(PageCache)。

为什么要有PageCache?

我们知道,磁盘的读取速度相对于内存的读取速度慢了非常多,所以我们能把“读写磁盘”这个过程替换成“读写内存”就好了,所以我们引用了PageCache这个机制来提高磁盘I/O性能,只要将频繁访问的数据存放在PageCache中,就能显著减少对磁盘的访问次数从而提高性能。

主要的问题是,内存的大小远远小于磁盘,那么应该选择哪些数据拷贝到内存中呢?

我们知道程序运行的时候,是具有“局部性”的,即刚被访问的数据在短时间内再次被访问的概率很高,于是我们就将这部分数据存放在PageCache中,当空间不足就淘汰掉最久未访问的数据。

所以,当系统需要读取数据的时候,应该先从PageCache中找,如果没有命中就从磁盘中读取。

还有一点,读取磁盘数据的时候,需要找到数据所在的位置,对于机械硬盘就需要通过磁头旋转到数据所在的盘去,再开始“顺序”读取数据,这个过程是非常耗时的,为了降低这里的能耗输出,PageCache使用了[预读功能]

比如,假设read方法每次会读取32KB的字节,虽然read刚开始只会读0~32KB的字节,但是内核会将后面的32~64KB的数据也读取到PageCache,这样子当后面需要读取该数据时,访问的成本就很低了。

于是,PageCache的优点主要有两个:

  • 缓存最近被访问的数据
  • 预读功能

2、DMA技术参与的磁盘I/O

DMA(Direct Memory Access)是一种用于数据传输的技术,它允许某些硬件子系统直接与内存进行通信,而无需经过CPU,这种技术带来的显著优点就是提高了数据传输的效率,减轻了CPU的负担。

有DMA技术参与的数据传输流程大致步骤如下:

  • 用户进程调用系统函数read()方法,向操作系统请求读取数据,然后进程被挂起进行阻塞等待,此时系统从用户态转入到内核态
  • CPU收到请求后,进一步将IO请求发送给DMA,此时CPU就可以返回去做其他的事情
  • DMA进一步将IO请求发送给磁盘
  • 磁盘收到请求后,将数据准备好放在磁盘控制器缓冲区中,接着通知DMA控制器
  • DMA控制器将数据从磁盘缓冲区拷贝到自己的内核缓冲区中,此时不占用CPU
  • 当DMA读取完数据,就将中断信号发送给CPU
  • CPU知道数据准备完毕后,就将数据从内核缓冲区拷贝到用户缓冲区,完成read()调用的返回

可以看到,并不是说整个过程都不需要CPU的参与了,只是CPU不必再参与到“将数据从磁盘控制器缓冲区拷贝到内核空间”的工作了,这部分工作全程都由DMA完成。但是CPU在这个过程中也是必不可少的,传输什么数据、从哪里运输到哪里都需要CPU来告诉DMA控制器。

这部分带来的性能优化很大的原因是因为,搬运数据的时耗主要是由磁盘的读取速度来决定的,我们知道磁盘的读取速度相对于内存、CPU的运行速率相差是非常巨大的,将CPU从这部分时间抽出来去做其他的任务就能提高了整体的系统性能。

3、文件传输

3.1、传统的文件传输性能之差

在介绍完DMA技术之后,我们继续往上,来学习当需要进行一次网络I/O的时候,系统内部的文件传输原理。

当服务端需要提供文件传输的功能的时候,我们能想到的一个简单的方式是:将磁盘上的文件读取出来,然后通过网络协议发送给客户端。

传统I/O的工作方式是,数据读取和写入是从用户空间到内核空间的来回复制,而内核空间的数据是通过操作系统的I/O接口从磁盘读取或者写入。

一般我们会用到read、write两个系统调用,该调用发生的事情可以用下图来描述。

在一套调用中,系统总共发生了4次用户态和内核态之间的切换。上下文切换的成本并不小,一次切换要几十纳秒到几十微秒,虽然时间看上去很短,但是在高并发的场景下这种积累会被放大,影响到系统的整体性能。

其次,还发生了4次数据拷贝,两次是DMA的拷贝,两次是CPU的拷贝。

从结果上来,我们为了完成一次数据从磁盘文件到网卡上的传输,一共经历了4次数据的拷贝,太多了!这些不必要的开销必然导致性能的下降。

所以想要提升文件传输的性能,就需要减少“用户态和内核态的切换”和“内存拷贝”的次数

3.2、如何优化文件传输的性能

首先先从减少“用户态与内核态的切换”的次数来入手。

读取磁盘的数据的时候,之所以要进行上下文的切换,是因为用户空间没有权限去操作磁盘或者网卡,内核的权限最高,这些操作应该都需要交由操作系统内核完成才能保证系统的安全性。所以当需要进行一些内核参与才能完成的任务的时候,就需要去进行系统函数的调用。

所以,想要减少上下文切换的次数,就要减少系统调用的次数。

接着,来看如何减少“数据拷贝”的次数

在刚刚的文件传输中我们知道,我们只需要达成数据从磁盘到达网卡的这一个“结果”,对于中间的数据从内核搬运到用户缓冲区,又从用户缓冲区搬运到内核缓冲区,这一部分我们是不需要的,因为我们不需要对数据的中间加工

于是,我们得出结论,用户的缓冲区在这里是没有必要存在的。

3.3、零拷贝技术

什么是零拷贝技术?

零拷贝技术是一种用于提高数据传输效率的技术,它通过减少不必要的数据拷贝,降低CPU的负担,从而提高系统性能。⚠️注意,零拷贝并不是说真的“不需要进行数据拷贝了”,而是消除了冗余的拷贝操作。

零拷贝技术的实现方式通常有两种:

  • mmap+write
  • sendfile

下面我们来探讨他们的实现原理。

3.3.1、mmap+wrtie

使用mmap()+write的方式为我们减少了一次数据拷贝的过程,可以将mmap()代替read()的调用,其实现原理是将内核缓冲区里的数据“映射”到用户空间,这样就减少了系统内核与用户空间之间多余的数据拷贝操作。

具体的过程如下:

  • 系统进程调用了mmap()后,DMA会把数据拷贝到内核的缓冲区中,接着进程和操作系统“共享”了这个缓冲区
  • 进程再调用write(),操作系统直接将内核缓冲区的数据拷贝到socket缓冲区中,这一切都发生在内核态,CPU来参与数据搬运
  • 最后,再把内核的socket缓冲区里的数据拷贝到网卡的缓冲区中,这个过程由DMA搬运

通过这个操作,我们成功减少了1次的数据拷贝过程,但是任然需要4次的上下文切换,因为系统调用还是2次。

这里的“零拷贝”指的是数据从用户态和内核态之间的零拷贝,还是文字游戏呀。

3.3.2、sendfile

在linux内核版本2.1中,提供了一个专门发送文件的系统调用函数sendfile(),形式如下

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

其前两个参数分别是目的端和源端的文件描述符,后面两个参数是源端的偏移量和复制数据的长度,返回值是实际复制数据的长度。

首先,它替代了前面的read()write()这两个系统调用,这样就可以减少了一次系统调用,从而减少了2次上下文切换的开销。

其次,该系统调用可以直接把内核缓冲区里的数据拷贝到socket缓冲区里,不再拷贝到用户态,这样子就只有3次数据拷贝了。

然而这还并不是零拷贝技术的“完全体”,如果网卡支持SG-DMA(The Scatter-Gather DMA)技术,sendfile()可以进一步减少通过CPU将数据从内核缓冲区拷贝到socket缓冲区的过程

具体流程如下图。

这就是真正意义上的零拷贝技术,在全过程中没有通过CPU来搬运数据,所有的数据都是通过DMA来进行传输的。

零拷贝技术的文件传输方式相比于传统的文件传输方式,减少了2次上下文切换和2次数据拷贝次数就可以完成数据传输,且数据拷贝的过程都是由DMA来搬运。

所以总体来看,零拷贝技术可以把文件传输的性能提高至少一倍以上。

4、使用零拷贝技术的项目

消息队列Kafka就利用了零拷贝技术,从而大幅度提升了I/O的吞吐率,这就是Kafka在处理海量数据这么快的原因。

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

long transferForm(FileChannel fileChnnel, long position, long count) throws IOException{
	return fileChannel.transferTo(position, count, sokcetChnnel);
}

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

下面是一张在同样的硬件条件下,使用了零拷贝技术的文件传输上的性能差异数据图,使用了零拷贝技术可以缩短65%的传输时间,大幅度提升了机器传输数据的吞吐量。

Nginx也支持零拷贝技术,一般默认是开启零拷贝技术,有利于提高文件传输的效率。

5、大文件传输的方式

在1.1节中,我们聊到了PageCache机制,但是当我们需要去传输大文件(GB级别的文件)的时候,PageCache就起不到作用,因为它无法就存储如此多的数据,那么就会白白浪费掉DMA多做的一次数据拷贝,造成性能的降低。

由于文件太大,会带来两个问题:

  • PageCache由于长时间被大文件占据,其他「热点」的小文件就可能无法充分的利用PageCache的优势,如此磁盘读写的性能就会下降了。
  • PageCache中的大文件数据,没有享受到缓存带来的优势,但却耗费DMA多拷贝到PageCache一次。

所以,对于大文件的传输,我们不应该使用到零拷贝技术。

回顾最初的read()调用,我们知道当进程需要读取数据时,会阻塞地等待read()调用的返回,针对这一段时间的优化,我们可以采用异步I/O来解决,其工作方式如下。

它将读操作分为两部分:

  • 前半部分,内核向磁盘发起读请求,但是可以不等待数据就为就返回,于是进程可以去处理其他的任务。
  • 后半部分,当内核将磁盘中的数据拷贝到进程缓冲区后,进程就接收到内核的通知,再去处理数据。

我们发现,异步IO没有涉及到PageCache,所以使用异步I/O就意味着绕开了PageCache。

绕开PageCache的I/O叫做直接I/O,使用PageCache的I/O叫做缓存I/O。通常,对于磁盘来说,异步I/O只支持直接I/O。

于是,在高并发的场景下,针对大文件的传输的方式,应该使用[异步I/O+直接I/O]来替代零拷贝技术。

直接I/O应用场景常见的两种:

  • 应用程序已经实现了磁盘数据的缓存,那么可以不需PageCache再次缓存,减少额外的性能损耗。
  • 传输大文件时,由于大文件难以命中PageCache,而且会占满缓存导致“热点”文件无法充分的利用,从而增大了性能的开销,因此应该直接使用直接I/O。

另外,由于直接I/O绕开了PageCache,就无法享受内核的这两点的优化:

  • 内核的I/O调度算法会缓存尽可能多的I/O请求在PageCache中,最终「合并」成一个更大的I/O请求再发给磁盘,为了减少磁盘的寻址操作。
  • 内核也会「预读」后续的I/O请求放在PageCache中,一样是为了减少对磁盘的操作。

6、总结

早期的I/O操作,内存与磁盘的数据传输的工作都是CPU完成的,而此时CPU不能执行其他的任务,会浪费CPU的资源。

为了优化这一个问题,DMA技术诞生了,每一个I/O设备都有自己的DMA控制器,CPU只需要告诉DMA控制器需要搬运什么数据、从哪里搬运到哪里即可,于是CPU不再参与数据的传输工作。

传统的IO工作方式中,系统从硬盘读取数据,再通过网卡向外发送,需要4次上下文切换,和4次数据拷贝,其中2次数据拷贝发生在内存的缓冲区和对应的硬件设备之间,这个由DMA来完成;另外两次发生在内核态和用户态之间,由CPU来完成数据搬运。

为了提高文件传输的性能,引入了零拷贝技术,通过系统调用(sendfile方法)合并了磁盘读取与网络发送两个操作,降低了上下文切换次数和数据拷贝次数。

Kafka和Nginx都实现了零拷贝技术,大大提高了文件传输的性能。

零拷贝技术是基于PageCache的,PageCache会缓存最近访问的数据,提升了访问缓存数据的性能,同时为了解决机械硬盘寻址慢的问题,还协助I/O调度算法实现了IO合并与预读,这也是顺序读比随机读性能好的原因。这些优势进一步提升了零拷贝的性能。

需要注意的是,零拷贝技术是不允许进程对文件内容进一步加工的,使用sendfile只能拿到发送数据的长度,而不能获取数据的具体消息。

当传输大文件时,不能使用零拷贝,因为可能由于PageCache被大文件占据,许多「热点」的小文件无法利用到PageCache导致对磁盘的访问增加。对于大文件传输应该使用「异步IO+直接IO」的方式。

摘抄总结至:小林coding

标签:PageCache,技术,学习,内核,磁盘,拷贝,数据,CPU
From: https://www.cnblogs.com/MelonTe/p/18516984

相关文章

  • # 20222322 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    1.实验内容1.1实验内容(1)使用msfvenom和msf编码器生成文件使用msfvenom生成exe文件,并进行编码。生成jar文件,用于Java环境下的攻击。生成PHP文件,用于Web服务器上的攻击。(2)使用Veil工具生成恶意代码下载并安装Veil-Evasion,使用Veil生成恶意代码。(3)使用C+shellco......
  • 基于深度学习的舆论分析与检测系统应用与研究
    【1】系统介绍研究背景随着互联网技术的迅猛发展和社会媒体平台的普及,信息传播的速度和范围达到了前所未有的水平。这一变化不仅极大地丰富了人们的社交生活,也为社会科学研究提供了新的视角和工具。舆论分析作为社会科学研究的一个重要分支,其目的是通过收集和分析网络上的......
  • Redis学习:BigKey、缓存双写一致性更新策略和案例
    Redis学习:BigKey、缓存双写一致性更新策略和案例文章目录Redis学习:BigKey、缓存双写一致性更新策略和案例1.BigKey2.缓存双写一致性更新策略3.缓存双写一致性案例1.BigKey面试题MoreKey不可以使用keys*,要使用SCAN基于游标来查询所有的key通过在redis.con......
  • 2024博鳌新型电力系统国际论坛——电力系统与新能源技术创新论坛 2024 Boao New Pow
    @目录一、会议详情二、重要信息三、大会介绍四、出席嘉宾五、征稿主题一、会议详情二、重要信息大会官网:https://ais.cn/u/vEbMBz提交检索:EICompendex、IEEEXplore、Scopus大会时间:2024年12月8-12月10日大会地点:中国·海南博鳌三、大会介绍2023年3月22日至23日,2023......
  • Python学习15天
    if 条件表达式:(条件为真,执行代码块1,否则执行代码块2)   代码块1else:   代码块2#键盘输入成绩,若成绩大于60,输出及格,否则输出不及格score=int(input("请输入成绩:"))ifscore>60:   print("及格")else:   print("不及格")#键盘输入年份,判断是......
  • Python学习第14天
    ~:按位取反,把数据的每个二进制按位取反~5:5=00000101(原码=反码=补码)~5=11111010(补码)-(反码=补码-1)=11111001-原码=00000110=6~5=6print(~5)~-3=2print(~-3)-3>原码=10000011>反码=11111100>补码=11111101~-3=00000010(补码=反码=原码)=2&:按位与:两个值都为1则......
  • 学习笔记(十五):ArkUi-切换按钮 (Toggle)
    概述:提供状态按钮样式、勾选框样式和开关样式,一般用于两种状态之间的切换一、创建单选框接口形式如下:type为类型,支持三种1、Switch  不包含子组件Toggle({type:ToggleType.Switch,isOn:true}) 2、Checkbox不包含子组件Toggle({type:ToggleType.Checkbox,isOn:true}......
  • 20222401 2024-2025-1 《网络与系统攻防技术》实验三实验报告
    1.实验内容1.1实践内容正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧通过组合应用各种技术实现恶意代码免杀用另一电脑实测,在杀软开启的情况下,可运行并回连成功,注明电脑的杀软名称与版本1.2回答问题杀软是如何检测出恶意代码的?--特征码、启发......
  • shell学习
    set-e解释如果你是在引用shell脚本中的set-e,那么在shell中它的意思是“如果任何命令的退出状态非零,则立即退出脚本”。这种用法可以防止脚本在遇到错误时继续执行。declare-xARCH="arm"是什么意思?declare-xARCH="arm" 是一个在Bashshell中的命令,具体含义如下:......
  • 学习笔记(十四):ArkUi-单选框 (Radio)
    概述:单选框组件,通常用于提供相应的用户交互选择项,同一组的Radio中只有一个可以被选中。单选框组件不支持自定义样式一、创建单选框接口形式如下:value为单选框名称,group为单选框所在组的名称,同一个组内最多只有一个单选框为选中状态Radio(options:{value:string,group:......