零拷贝原理与实现
创始人
2024-04-15 16:08:37
0

1.传统拷贝

FileInputStream、FileOutputStream

read:将数据从磁盘读取到内核态缓冲区,再从内核态缓冲区拷贝到用户缓冲区

write:将数据从用户缓冲区写入到socket缓冲区,再从socket缓冲区写入到网卡设备

内核空间:

主要是提供进程调度、内存分配、连接硬件资源等功能

用户空间:

提供给应用程序的空间,不具有访问内核资源的权限。如果用户程序需要使用到内核空间的资源,则需要通过系统调用来完成。进程从用户空间切换到内核空间,完成相关操作后,再从内核空间切换到用户空间。

内核态:

进程运行在内核空间,被称为进程的内核态

用户态:

进程运行在用户空间,被称为进程的用户态。

DMA技术

DMA本质上是一块主板上独立的芯片,允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU的参与

主要流程就是:CPU通知DMA将磁盘数据拷贝到内核态后,CPU就可以无需等待执行其他任务。DMA通知磁盘将数据从磁盘放到磁盘缓冲区,磁盘完成后通知DMA,DMA将数据从磁盘缓冲区放到内内核缓冲区中。完成后,通知CPU来取数据。

1.应用程序调用read函数,向操作系统发起IO调用,上下文从用户态切换至内核态
2.DMA控制器把数据从磁盘中读取到内核缓冲区
3.CPU把内核缓冲区数据拷贝到用户应用缓冲区,上下文从内核态切换至用户态,此时read函数返回
4.用户应用进程通过write函数,发起IO调用,上下文从用户态切换至内核态
5.CPU将缓冲区的数据拷贝到socket缓冲区
6.DMA控制器将数据从socket缓冲区拷贝到网卡设备,上下文从内核态切换至用户态,此时write函数返回

 从这里可以看到做一次从磁盘到用户缓冲区的数据提取,需要cpu切换2次,cpu拷贝次数1次,DMA拷贝1次,如果是复制那就需要4次CPU切换,2次CPU拷贝,2次DMA拷贝,所以需要减少CPU在用户态与内核态之间的切换次数以及拷贝次数。

2. 零拷贝

什么是零拷贝?

零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,进而减少上下文切换以及CPU的拷贝时间,是IO操作优化技术。

虚拟内存

虚拟内存远大于物理内存空间。且多个虚拟内存可以映射同一个物理地址。这样当DAM将数据拷贝到内核缓存后,无需cpu将数据再拷贝到用户空间了。

实现零拷贝的方式

1. mmap + write

2. sendfile

3. 带有DMA收集拷贝功能的sendfile

MMAP拷贝 

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

1.用户进程通过调用mmap方法向操作系统内核发起IO调用,上下文从用户态切换至内核态
2.CPU利用DMA控制器,将数据从硬盘拷贝到内核缓冲区
3.上下文从内核态切换回用户态,mmap方法返回
4.用户进程通过调用write方法向操作系统内核再次发起IO调用,上下文从用户态切换至内核态
5.CPU将内核缓冲区的数据拷贝到socket缓冲区
6.CPU利用DMA控制器,将数据从socket缓冲器拷贝到网卡,上下文从内核态切换至用户态,write方法返回

使用mmap + write拷贝,CPU上下文切换4次,拷贝3次(1次CPU拷贝,2次DMA拷贝),由此可见减少了一次CPU拷贝。

sendfile拷贝

sendfile是Linux2.1版本后内核引入 的一个系统调用函数,原型如下

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

sendfile表示两个文件描述符之间传输数据,他是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之际七年的拷贝操作,因此可以用它来实现零拷贝。

1.用户进程发起sendfile系统调用,上下文从用户态切换至内核态
2.DMA控制器将数据从硬盘拷贝到内核缓冲区
3.CPU将读缓冲区中的数据拷贝到socket缓冲区
4.DMA控制器异步把数据从socket缓冲器拷贝到网卡
5.上下文从内核态切换至用户态,sendfile函数返回

使用sendfile拷贝,CPU上下文切换2次,拷贝3次 (1次CPU拷贝,2次DMA拷贝),由此可见减少了两次CPU切换,一次CPU拷贝。

sendfile +DMA scatter/gather实现的零拷贝

1.用户进程发起sendfile系统调用,上下文从用户态切换至内核态
2.DMA控制器将数据从磁盘拷贝到内核缓冲器
3.CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)直接发送到socket缓冲区
4.DMA控制器根据文件描述符信息直接把数据从内核缓冲区拷贝到网卡
5.上下文切换至用户态,sendfile返回

sendfile +DMA scatter/gather,只产生2次CPU切换,2次DMA拷贝,这才是真正意义的零拷贝。

拷贝速度对比

Java中拷贝文件的方式

使用文件大小:2657KB

使用FileInputStream、FileOutputStream传统拷贝

private static void fileCopy(){FileInputStream fileInputStream = null;FileOutputStream fileOutputStream = null;try {Long t1 = System.currentTimeMillis();fileInputStream = new FileInputStream(new File("C:\\Users\\XXX\\IdeaProjects\\text.txt"));fileOutputStream = new FileOutputStream(new File("C:\\Users\\XXX\\IdeaProjects\\FileOutputStream.txt"));int readData = 0;byte[] buf = new byte[1024*1024*10];while (-1 != (readData = fileInputStream.read(buf))) {fileOutputStream.write(readData);}Long t2 = System.currentTimeMillis();System.out.println(t2-t1);} catch (Exception e) {e.printStackTrace();}  finally {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}

耗时:11

MMAP(FileChannle的read、write)拷贝

private static void mmapCopy() {FileInputStream fileInputStream = null;FileOutputStream fileOutputStream = null;FileChannel readChannel = null;FileChannel wirteChannel = null;try {Long t1 = System.currentTimeMillis();fileInputStream = new FileInputStream(new File("C:\\Users\\xxx\\IdeaProjects\\text.txt"));fileOutputStream = new FileOutputStream(new File("C:\\Users\\xxx\\IdeaProjects\\MMAP.txt"));readChannel = fileInputStream.getChannel();wirteChannel = fileOutputStream.getChannel();int n = 0;ByteBuffer buf = ByteBuffer.allocate(1024*1024*10);while (readChannel.read(buf) != -1) {buf.flip();wirteChannel.write(buf);buf.clear();wirteChannel.force(true);}Long t2 = System.currentTimeMillis();System.out.println(t2 - t1);} catch (IOException e) {e.printStackTrace();} finally {try {readChannel.close();} catch (IOException e) {e.printStackTrace();}try {wirteChannel.close();} catch (IOException e) {e.printStackTrace();}try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}

 耗时:27

sendfile拷贝(FileChannle的transferTo)

private static void senfileCopy() {FileInputStream fileInputStream = null;FileOutputStream fileOutputStream = null;FileChannel readChannel = null;FileChannel wirteChannel = null;try {fileInputStream = new FileInputStream(new File("C:\\Users\\XXX\\IdeaProjects\\text.txt"));fileOutputStream = new FileOutputStream(new File("C:\\Users\\XXX\\IdeaProjects\\SendFile.txt"));readChannel = fileInputStream.getChannel();wirteChannel = fileOutputStream.getChannel();Long t1 = System.currentTimeMillis();long len = readChannel.size();long position = readChannel.position();readChannel.transferTo(position,len,wirteChannel);Long t2 = System.currentTimeMillis();System.out.println(t2 - t1);} catch (IOException e) {e.printStackTrace();} finally {try {readChannel.close();} catch (IOException e) {e.printStackTrace();}try {wirteChannel.close();} catch (IOException e) {e.printStackTrace();}try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}try {fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}}

 耗时:3

在小数据量下传统方式和mmap方式没有太大的区别,但是sendFile方式差距还是比较明显的。

传统拷贝mmap+writesendfilesendfile+scatter、gather
CPU切换次数4422
CPU拷贝次数2110
DMA拷贝次数2222
原因存在用户缓冲区和内核缓冲区,使用DMA技术,CPU需要将数据从内核缓冲区到用户缓冲区来回读取增加虚拟内存地址映射,CPU直接通过映射招到内核态的数据copy到Socket缓冲区使用sendfile减少CPU的切换次数使用sendfile减少CPU的切换次数,且CPU不再将数据复制到Socket缓冲区,而是把数据的位置和长度给到Socket缓冲区,由DMA根据位置和长度直接从内核缓冲区复制到磁盘
系统调用read/writemmap/writesenfilesendfile+scatter/gather

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...