一文彻底弄懂零拷贝原理以及java实现

一文彻底弄懂零拷贝原理以及Java实现

什么是零拷贝

在传统的计算机系统中,在文件从磁盘到达应用程序之前,文件会被存储到内核缓冲区中。当应用程序需要访问文件时,它必须从内核缓冲区将文件读入应用程序的缓冲区。这种方式称之为“传统的拷贝方式”。

但是,“传统的拷贝方式”存在以下问题:

  1. 内存中存在多个拷贝:原始数据的一个拷贝保存在磁盘中,一个拷贝保存在内核缓冲区中,另一个拷贝保存在应用程序缓冲区中。
  2. 额外的 CPU 开销:每当一个进程读写文件时,内核必须从内核缓冲区向进程缓冲区复制数据,这需要 CPU 资源。
  3. 多个拷贝增加了上下文切换:多个拷贝需要上下文切换,同时也会增加网络、存储设备、操作系统之间的交互,这都会导致数据不连续,效率低下。

为了解决这些问题,Linux 系统中引入了彻底的零拷贝技术。这样一来,用户空间的应用程序可以直接访问内核缓冲区中的数据,避免了不必要的拷贝将数据发送给网络和存储设备。

两种实现零拷贝的方式

介绍一下实现零拷贝的两种常见方式:文件描述符传递(sendfile)和内存映射文件(mmap)。

文件描述符传递(sendfile)

“文件描述符传递”利用两个系统调用 —— sendfile 和 splice。简单来说,sendfile 属于 posic/ansi 标准,而 splice 属于 unix 标准,它们都能够减少 CPU 的消耗并避免数据拷贝。

sendfile 函数是把一个文件描述符中的数据传到另一个文件描述符中,它调用 sendfile 函数的文件描述符可以是 socket 或者是普通文件。一旦 socket 和 file 都打且准备好了,数据就可以通过 Socket 并使用 TCP 协议传输。

splice 函数可以把两个管道中数据传输。使用 splice() 函数的优点是在数据移动时不需要将数据从内核空间移动到用户空间,再从用户空间移动到新的文件描述符。splice 可以直接从一个文件描述符移动到另一个文件描述符。splice 功能上和“内存映射文件”有一定的类似之处,它们都是可以避免数据在用户空间和内核空间来回拷贝。

示例:

public static void sendFileBySendFile(File file, OutputStream os) throws IOException {
    try (FileInputStream fis = new FileInputStream(file); FileChannel channel = fis.getChannel()) {
        long length = file.length();
        WritableByteChannel wChannel = Channels.newChannel(os);
        channel.transferTo(0, length, wChannel);
    }
}

内存映射文件(mmap)

mmap() 系统调用是将一个文件或者其他对象映射到内存中,通常是将一个文件映射到被调用进程的地址空间中,从而可以直接访问文件。内存映射文件的实现可以无需再次拷贝数据,减少了 I/O 操作和消耗。

当进程请求一段文件的内存映射时,内核会以页(通常为 4KB )为单位来设置映射。

映射区域可以当作是文件的缓存,内核也会根据内存写入数据情况而实时刷写到磁盘上。如此一来,进程就可以避免来回复制数据。这里的零拷贝是多个客户端或者系统之间的零拷贝,并不是零次拷贝。

示例:

BufferedInputStream in = new BufferedInputStream(new FileInputStream(new File(filePath)));
FileChannel infileChannel = new FileInputStream(new File(filePath)).getChannel();
MappedByteBuffer buffer = infileChannel.map(FileChannel.MapMode.READ_ONLY, 0, infileChannel.size());

总结

通过零拷贝技术,我们可以让磁盘、网络等协作更高效,在数据的读写和处理方面带来好处。在一些高性能在线服务的场景下,零拷贝技术的优势得以更好的体现。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:一文彻底弄懂零拷贝原理以及java实现 - Python技术站

(0)
上一篇 2023年6月28日
下一篇 2023年6月28日

相关文章

  • Java泛型之协变与逆变及extends与super选择

    Java泛型中的协变与逆变是很重要的概念,也常被面试官问到。本篇文章将带你深入理解这些概念,并介绍有关extends和super的最佳实践。 什么是Java泛型中的协变和逆变? 在介绍协变和逆变之前,我们需要先了解两个术语: 子类型和超类型。子类型是指一个类派生自另一个类,而超类型是指另一个类派生自某一个类。这两个概念很重要,后面我们会经常用到。 在Java…

    other 2023年6月27日
    00
  • 分享面试官常用16个c/c++面试题

    分享面试官常用16个C/C++面试题攻略 在C/C++面试中,经常会涉及一些基本的数据结构、算法、指针等等的基本原理。下面是面试官常用的16个C/C++面试题,介绍一下如何准备和应对这些问题。 1. 什么是指针? 指针是一个特殊的变量,它可以保存变量的地址,从而让程序员操作内存中的数据。 2. 指针和数组有什么关系? 指针和数组非常类似,实际上数组名就是一个…

    other 2023年6月26日
    00
  • Javaweb学习笔记3—Serverlet

    Javaweb学习笔记3—Servlet的完整攻略 本文将为您提供Javaweb学习笔记3—Servlet的完整攻略,包括介绍、Servlet的生命周期、Servlet的使用方法和两个示例说明。 介绍 Servlet是JavaWeb中的一种技术,用于处理客户端请求和响应。Servlet可以接收来自客户端的请求,处理请求并生成响应。本文将介绍Servlet的生…

    other 2023年5月6日
    00
  • js的三种继承方式详解

    下面我将详细讲解 JavaScript 的三种继承方式。 1. 原型继承 原型继承是 JavaScript 中最基本的继承方式,它实现的原理是通过使用 prototype 属性。在原型继承中,子类的原型对象指向父类的实例对象,从而实现继承。 以下是一个实现原型继承的示例代码: function Person(name, age) { this.name = …

    other 2023年6月26日
    00
  • realme x手机上网慢怎么办?realme x上网慢解决方案

    当使用realme X手机上网时,遇到网速慢的情况,可能是由于网络信号较弱、手机设置问题、运营商网络问题等原因所导致。下面是一些可能的解决方案: 1. 检查网络信号 如果在室外、高楼等不稳定的网络环境下使用,请考虑尝试切换到其它的位置扩大网络覆盖范围。 如果仍然遇到信号不佳的情况,请联系运营商客服咨询安装信号增强器等相关设备。 2. 清理手机缓存和垃圾文件 …

    other 2023年6月26日
    00
  • 基于Vue技术实现递归组件的方法

    基于Vue技术实现递归组件的方法,主要是使用Vue的组件化特性和递归引用组件的方式来实现。下面,我们来详细讲解该攻略。 1.创建组件 首先,我们需要创建一个组件,用来展示递归的效果。在组件中,我们需要定义递归的终止条件,以及如何渲染递归的子组件。具体代码如下: <template> <div> <span>{{ item.…

    other 2023年6月27日
    00
  • oracle初学之where的使用

    Oracle初学之WHERE的使用 在Oracle数据库中,WHERE子句用于过滤查询结果,它可以根据指定的条件从表中选择特定的行。以下是Oracle初学之WHERE使用的详细攻略。 步骤1:了解WHERE子句 WHERE子句是SELECT语句的一部分,它用于指定查询条件WHERE子句可以使用比较运算符、逻辑运算符和运算符等来构建查询条件。 步骤2:使用WH…

    other 2023年5月9日
    00
  • vivo nex如何开启开发者选项?vivo nex开发者选项开启教程

    以下是详细讲解“vivo nex如何开启开发者选项?vivo nex开发者选项开启教程”的完整攻略。 什么是开发者选项 开发者选项是 Android 系统中一个标准的功能,它为应用程序开发人员提供了一些高级的选项和功能。这些功能包括手动设置 USB 调试模式、模拟位置信息、绘制应用程序边界、指针位置和程序运行时分析信息等。 如何开启 vivo nex 的开发…

    other 2023年6月27日
    00
合作推广
合作推广
分享本页
返回顶部