【Netty】mmap 和 sendFile 零拷贝原理

【Netty】mmap 和 sendFile 零拷贝原理一、零拷贝简介、二、传统BIO数据拷贝分析(4拷贝4切换)、三、mmap内存映射(3拷贝4切换)、四、sendFile函数(Linux2.1优化)(3拷贝2切换)、五、sendFile函数(Linux2.4优化)(2拷贝2切换)、

大家好,又见面了,我是你们的朋友全栈君。

一、 零拷贝 简介


零拷贝作用 : 在网络编程中 , 如果要进行性能优化 , 肯定要涉及到零拷贝 , 使用零拷贝能极大的提升数据传输性能 ;

零拷贝类型 : mmap ( 内存映射 ) 和 sendFile;

数据角度分析 : 在零拷贝机制中 , 整个数据在内存中只有一份数据 , 非零拷贝机制中 , 内核缓冲区 , 用户缓冲区 , Socket 缓冲区 , 各有一份数据 ;

零拷贝指的是没有 CPU 拷贝 , 都是 DMA ( 直接内存访问 ) 拷贝 ;

零拷贝性能优势 : 没有复制数据带来的内存开销 , 没有 CPU 拷贝 , 直接节省了大量 CPU 计算资源 ;

二、 传统 BIO 数据拷贝分析 ( 4拷贝 4切换 )


传统 BIO 数据拷贝代码示例 :

package kim.hsl.nio.zerocopy;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.Socket;

public class BIOClientDemo { 
   
    public static void main(String[] args) { 
   
        try { 
   
            // 客户端与服务器端连接过程忽略, 主要是分析数据拷贝过程
            Socket socket = new Socket();
            InetSocketAddress inetSocketAddress =
                    new InetSocketAddress(Inet4Address.getLocalHost(), 8888);
            socket.connect(inetSocketAddress);

            // 分析下面过程中, 数据拷贝次数, 和用户态与内核态的转换次数

            // 1. 从文件中读取数据
            FileInputStream fileInputStream = new FileInputStream("file.txt");
            byte[] buffer = new byte[1024];

            // 首先将硬盘中的文件, 进行 DMA 拷贝, 此处对应 read 方法, 
            // 将文件数据从硬盘中拷贝到 内核缓冲区 ( 用户态切换成内核态 )
            // 将内核缓冲区中的数据, 通过 CPU 拷贝 方式, 拷贝到 用户缓冲区 ( 内核态切换成用户态 )
            int readLen = fileInputStream.read(buffer);

            // 2. 写出数据到服务器
            // 将用户缓冲区中的数据, 再次通过 CPU 拷贝方式, 拷贝到 Socket 缓冲区 ( 用户态切换成内核态 )
            // 再次使用 DMA 拷贝, 将 Socket 缓冲区中的数据拷贝到 协议栈 ( Protocol Engine ) 中
            socket.getOutputStream().write(buffer, 0, readLen);
        } catch (IOException e) { 
   
            e.printStackTrace();
        }
    }
}

分析上述代码中数据拷贝次数 , 用户态与内核态状态切换 ;

1 . fileInputStream.read(buffer) 操作数据拷贝及状态转换分析 :

① 硬盘 ( 初始用户态 ) -> 内核缓冲区 ( 内核态 ) : 首先将硬盘中的文件 , 进行 DMA [ 1 ] ^{[1]} [1] 拷贝 , 此处对应 read 方法 , 将文件数据从硬盘中拷贝到 内核缓冲区 ; ( 用户态切换成内核态 )

② 内核缓冲区 ( 内核态 ) -> 用户缓冲区 ( 用户态 ) : 将内核缓冲区中的数据 , 通过 CPU 拷贝 方式 , 拷贝到 用户缓冲区 ; ( 内核态切换成用户态 )

2 . socket.getOutputStream().write(buffer, 0, readLen) 操作数据拷贝及状态转换分析 :

① 用户缓冲区 ( 用户态 ) -> Socket 缓冲区 ( 内核态 ) : 将用户缓冲区中的数据 , 再次通过 CPU 拷贝 方式 , 拷贝到 Socket 缓冲区 ; ( 用户态切换成内核态 )

② Socket 缓冲区 ( 内核态 ) -> 协议栈 : 再次使用 DMA [ 1 ] ^{[1]} [1] 拷贝 , 将 Socket 缓冲区中的数据拷贝到 协议栈 ( Protocol Engine ) 中 ;

3 . 总结 : 上述进行了 4 4 4 次拷贝 , 3 3 3 次用户态与内核态之间的状态切换 , 代价很高 ;

① 拷贝次数分析 : 开始时数据存储在 硬盘文件 中 , 直接内存拷贝 ( Direct Memory Access )内核缓冲区 , CPU 拷贝用户缓冲区 , CPU 拷贝Socket 缓冲区 , 直接内存拷贝 ( Direct Memory Access )协议栈 ;

硬盘文件 -> 内核缓冲区 ( 内核空间 ) -> 用户缓冲区 ( 用户空间 ) -> Socket 缓冲区 ( 内核空间 ) -> 协议栈

② 状态改变分析 : 开始运行的是用户应用程序 , 起始状态肯定是用户态 , 之后将硬盘文件数据拷贝到内核缓冲区后 , 转为内核态 , 之后又拷贝到了用户缓冲区 , 转为用户态 ; 数据写出到 Socket 缓冲区 , 又转为内核态 , 最后再切换成用户态 , 执行后续应用程序代码逻辑 ;

用户态 -> 内核态 -> 用户态 -> 内核态 -> 用户态

[ 1 ] [1] [1] DMA 全称 ( Direct Memory Access ) , 直接内存拷贝 , 该拷贝通过内存完成 , 不涉及 CPU 参与 ;

三、 mmap 内存映射 ( 3拷贝 4切换 )


将硬盘中的文件映射到 内核缓冲区 , 用户空间中的应用程序也可以访问该 内核缓冲区 中的数据 , 使用这种机制 , 原来的 4 4 4 次数据拷贝减少到了 3 3 3 次 ,

1 . mmap 数据拷贝过程 :

① 硬盘文件 -> 内核缓冲区 : 硬盘文件数据 , DMA 拷贝到 内核缓冲区 中 , 应用程序可以直接访问该 内核缓冲区中的数据 ;

② 内核缓冲区 -> Socket 缓冲区 : 内核缓冲区 数据 , 通过 CPU 拷贝到 Socket 缓冲区 ;

③ Socket 缓冲区 -> 协议栈 : Socket 缓冲区 数据 , 通过 DMA 拷贝到 协议栈 ;

硬盘文件 -> 内核缓冲区 ( 内核空间 ) -> Socket 缓冲区 ( 内核空间 ) -> 协议栈

2 . mmap 状态切换 : 其状态切换还是 3 3 3 , 由初始状态 用户态 , 在拷贝数据到内核缓冲区时 , 切换成内核态 , 访问该内核缓冲区数据时 , 又切换成用户态 , 将数据拷贝到 Socket 缓冲区时 , 切换成内核态 , 最后再切换成用户态 , 执行后续应用程序代码逻辑 ;

用户态 -> 内核态 -> 用户态 -> 内核态 -> 用户态

四、 sendFile 函数 ( Linux 2.1 优化 ) ( 3拷贝2切换 )


sendFile 是 Linux 提供的函数 , 其实现了由 内核缓冲区 直接将数据拷贝到 Socket 缓冲区 , 该操作直接在内核空间完成 , 不经过用户空间 , 没有用户态参与 , 因此 减少了一次用户态切换 ;

此次优化 , 由原来的 4 4 4 次拷贝 , 3 3 3 次状态切换 , 变成 3 3 3 次拷贝 , 2 2 2 次状态切换 ;

1 . sendFile 函数 数据拷贝分析 :

① 硬盘文件 -> 内核缓冲区 : 硬盘文件数据 , DMA 拷贝到 内核缓冲区 中 ;

② 内核缓冲区 -> Socket 缓冲区 : 内核缓冲区 数据 , 通过 CPU 拷贝到 Socket 缓冲区 ;

③ Socket 缓冲区 -> 协议栈 : Socket 缓冲区 数据 , 通过 DMA 拷贝到 协议栈 ;

硬盘文件 -> 内核缓冲区 ( 内核空间 ) -> Socket 缓冲区 ( 内核空间 ) -> 协议栈

2 . sendFile 函数 状态切换分析 : 其状态切换只有 2 2 2 , 由初始状态 用户态 , 在拷贝数据到内核缓冲区时 , 切换成内核态 , 在内核态直接将数据拷贝到 Socket 缓冲区时 , 还是处于内核状态 , 之后拷贝到协议栈时 , 变成用户状态 ;

用户态 -> 内核态 -> 用户态

五、 sendFile 函数 ( Linux 2.4 优化 ) ( 2拷贝 2切换 )


sendFile 是 Linux 提供的函数 , 其在 Linux 2.4 版本中 , 直接将数据从 内核缓冲区 拷贝到 协议栈 中 ;

此次优化 , 由原来的 4 4 4 次拷贝 , 3 3 3 次状态切换 , 变成 2 2 2 次拷贝 , 2 2 2 次状态切换 ;

1 . sendFile 函数 数据拷贝分析 : 全称 DMA 拷贝 , 没有 CPU 拷贝 ;

① 硬盘文件 -> 内核缓冲区 : 硬盘文件数据 , DMA 拷贝到 内核缓冲区 中 ;

② 内核缓冲区 -> -> 协议栈 : 通过 DMA 拷贝 , 将 内核缓冲区 中的数据直接拷贝到 协议栈 ;

硬盘文件 -> 内核缓冲区 ( 内核空间 ) -> 协议栈

2 . sendFile 函数 状态切换分析 : 其状态切换只有 2 2 2 , 由初始状态 用户态 , 在拷贝数据到内核缓冲区时 , 切换成内核态 , 在内核态直接将数据拷贝到协议栈时 , 变成用户状态 ;

用户态 -> 内核态 -> 用户态

3 . 少量 CPU 拷贝 : 该机制还存在少量的 CPU 拷贝 , 其 对性能的消耗忽略不计 ; 这些 CPU 拷贝操作是从 内核缓冲区 中将数据的长度 ( Length ) , 偏移量 ( Offset ) 拷贝到 Socket 缓冲区 ;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/139914.html原文链接:https://javaforall.net

(1)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 小树311_森林小道

    小树311_森林小道原题链接森森开了一家快递公司,叫森森快递。因为公司刚刚开张,所以业务路线很简单,可以认为是一条直线上的N个城市,这些城市从左到右依次从0到(N−1)编号。由于道路限制,第i号城市(i=0,⋯,N−2)与第(i+1)号城市中间往返的运输货物重量在同一时刻不能超过C​i​​ 公斤。公司开张后很快接到了Q张订单,其中j张订单描述了某些指定的货物要从S​j​​ 号城市运输到T​j​​ 号城市。这里我们简单地假设所有货物都有无限货源,森森会不定时地挑选其中一部分货物进行运输。安全起见,这些货物不会在中

    2022年8月9日
    3
  • VB学习之路 ——基本语句

    VB学习之路 ——基本语句一:选择结构问题总结1.一个很简单的If(表达式)……..Then的问题,在VB的程序编写时候,如果在if….Then后面只有一条需要执行的语句,并且将执行的一条语句直接放在了Then的后面,则不需要后面加上EndIf,加上就报错。即使要执行的语句有多条如果非要放在Then后面不加EndIf就必须每条语句之间用冒号间隔。如果将执行的语句放在了Then的后面,即…

    2022年6月21日
    28
  • 如何快速搭建图片服务器[通俗易懂]

    前言最近学习一个分布式集群的项目,正常一般的工程是把图片放在web项目的自身服务器的工程中,但在集群环境下,会出现找不到图片的情况。代码参考:https://github.com/zyjcxc/taotao.git比如:解决办法:linux做磁盘的映射,说能解决,但服务器多了也不好弄,所以可以再搭建一个图片服务器图片服务器两个服务:http:可以使用nginx…

    2022年4月10日
    57
  • 先学java再学python_java和python应该先学好哪个呢

    先学java再学python_java和python应该先学好哪个呢java和python应该先学好哪个呢发布时间:2020-07-3011:21:41来源:亿速云阅读:82作者:清晨不懂java和python应该先学好哪个呢?其实想解决这个问题也不难,下面让小编带着大家一起学习怎么去解决,希望大家阅读完这篇文章后大所收获。作为一名Java程序员,肯定会建议你先学Java,然后再学Python,但如果你问一个Python程序员,可能会得到一个完全相反…

    2022年7月8日
    18
  • Jetson TX1开发笔记(二):TX1开发前必做的几件事

    Jetson TX1开发笔记(二):TX1开发前必做的几件事转载请注明作者和出处:http://blog.csdn.net/c406495762嵌入式平台:JestonTX1

    2022年6月23日
    23
  • 天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行拂乱其所为,所以动心忍性,增益其所不能

    天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤,空乏其身,行拂乱其所为,所以动心忍性,增益其所不能

    2021年11月15日
    42

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号