IO零拷贝

在介绍零拷贝之前我们先看看传统的 Java 网络 IO 编程是怎样的。

下面代码展示了一个典型的 Java 网络程序。

    File file = new File("index.jsp");RandomAccessFile rdf = new RandomAccessFile(file, "rw");byte[] arr = new byte[(int) file.length()];rdf.read(arr);Socket socket = new ServerSocket(8080).accept();socket.getOutputStream().write(arr);

程序中调用 RandomAccessFile 的 read 方法将 index.jsp 的内容读取到字节数组中。然后调用 write 方法将字节数组中的数据写入到 Socket 对应的输出流中发送给客户端。那么 Java 应用程序中的 read、write 方法对应到 OS 底层是怎样的呢。下图展示了这个过程。
在这里插入图片描述
图中上半部分记录了用户态和内核态的上下文切换。下半部分展示了数据的复制过程。上述 Java 代码对应的操作系统底层步骤:

  1. read 方法触发操作系统从用户态到切换到内核态。同时通过 DMA 的方式从磁盘读取文件到内核缓冲区。DMA(Direct Memory Access)是 l/O 设备与主存之间由硬件组成的直接数据通路。即不需要 CPU 拷贝数据到内存,而是直接由 DMA 引擎传输数据到内存。

  2. 紧接着发生第二次数据拷贝,即从内核缓冲区拷贝到用户缓冲区,同时发生一次内核态到用户态的上下文切换。

  3. 调用 write 方法时,触发第三次数据拷贝,即从用户缓冲区拷贝到 Socket 缓冲区。同时发生一次用户态到内核态的上下文切换。

  4. 最后数据从 Socket 缓冲区异步拷贝到网络协议引擎,这一步采用的是 DMA 方式。同时没有发生上下文切换。

  5. write 方法返回时,触发了最后一次内核态到用户态的切换。

由此可见,复制的操作太频繁,共有 2 次 DMA 拷贝、2 次 CPU 拷贝、4 次上下文切换。能否优化呢?

这就要介绍称之为"零拷贝"的技术。首先声明,零拷贝技术依赖底层 OS 内核提供的支持。Linux 中提供的这类支持有 mmap(),sendfile() 以及 splice() 系统调用。说白了就是减少数据在操作系统内核的缓冲区和用户应用程序地址空间的缓冲区之间进行拷贝。

mmap

mmap 通过内存映射,将文件通过 DMA 的方式映射到内核缓冲区。操作系统会把这段内核缓冲区与应用程序(用户空间)共享。这样,在进行网络传输时,就能减少内核空间到用户空间的拷贝次数。此时输出数据时只要从内核缓冲区拷贝到 Socket 缓冲区即可。可见减少了一次 CPU 拷贝,但是上下文切换次数并没有减少。整个过程共 2 次 DMA 拷贝,1 次 CPU 拷贝,4 次上下文切换。示意图如下。
在这里插入图片描述

sendFile

Linux 2.1 开始提供了 sendFile 函数,其基本原理是:数据根本不经过用户态,直接从 Kernel Buffer 进入到 Socket Buffer,并且由于和用户态完全无关,这就避免了一次上下文切换。下图展示了整个过程。磁盘中的数据通过 DMA 引擎从复制到内核缓冲区。调用 write 方法时从内核缓冲区拷贝到 Socket 缓冲区。由于在同一个空间,因此没有发生上下文切换。最后由 Socket 缓冲区拷贝到协议引擎。整个过程共发生了 2 次 DMA 拷贝,1 次 CPU 拷贝,3 次上下文切换。
在这里插入图片描述

在 Linux 2.4 版本中,进一步做了优化。从 Kernel Buffer 拷贝到 Socket Buffer 的操作也省了,直接拷贝到协议栈,再次减少了 CPU 数据拷贝。下图展示了整个流程。本地文件 index.jsp 要传输到网络中,只需 2 次拷贝。第一次是 DMA 引擎从文件拷贝到内核缓冲区;第二次是从内核缓冲区将数据拷贝到网络协议栈;内核缓存区只会拷贝一些元信息,比如 offset 和 length 信息到 SocketBuffer,基本无消耗。
在这里插入图片描述

综上所述,最后一种方式发生了 2 次 DMA 拷贝、0 次 CPU 拷贝、3 次上下文切换。这就是所谓的“零拷贝”实现。

总结:

因此零拷贝通常是站在操作系统的角度看,即整个过程中,内核缓冲区之间是没有重复数据的。同时伴随着更少的上下文切换。这就带来了 IO 性能质的提升!

实际开发中,mmap 和 sendFile 都有应用,可以认为是“零拷贝”的两种实现方式。它们都有各自的适用场景。mmap 更适合少量数据读写,sendFile 适合大文件传输。sendFile 可以利用 DMA 方式将内核缓冲区将数据拷贝到网络协议栈,减少 CPU 拷贝,而 mmap 则不能(必须从内核拷贝到 Socket 缓冲区)。

案例:

RocketMQ 在 CommitLog 和 CosumerQueue 的实现中都采用了 mmap。而 Kafka 的零拷贝实现则使用了 sendFile。

RocketMQ 和 Kafka 高性能的原因之一便是顺序写入和近似顺序读取 + 零拷贝。


引用:https://zhuanlan.zhihu.com/p/543661648

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/219387.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Tcl语言语法精炼总结

一、置换符号 1.变量置换 $ TCl解释器会将认为$后面为变量名,将变量名置换成它的值 2.命令置换 [] []内是一个独立的TCL语句 3.反斜杠置换 \ 换行符、空格、[、$等被TCL解释器当作特殊符号处理。加上反斜杠后变成普通字符 \t TAB \n 换行符 4.双引号 “” “…

鸿蒙开发之页面与组件生命周期

一、页面间的跳转 创建文件的时候记得选择创建page文件,这样就可以在main->resources->profile->main_pages.json中自动形成页面对应的路由了。如果创建的时候你选择了ArkTS文件,那么需要手动修改main_pages.json文件中,添加相应的…

关于大模型ChatGLM3-6B在CPU下运行

最近在调研市场上语言大模型,为公司的产品上虚拟人的推出做准备。各厂提供语言模型都很丰富,使用上也很方便,有API接口可以调用。但唯一的不足,对于提供给百万用户使用的产品,相比价格都比较贵。所以对ChatGLM3-6B的使…

基于pandoraNext使用chatgpt4

1.登陆GitHub 获取pandoraNext项目GitHub - pandora-next/deploy: Pandora Cloud Pandora Server Shared Chat BackendAPI Proxy Chat2API Signup Free PandoraNext. New GPTs(Gizmo) UI, All in one! 在release中选择相应版本操作系统的安装包进行下载 2.获取license_…

最新鸿蒙HarmonyOS4.0开发登陆的界面1

下载deveco-studio 说明一下,本人只是学习中,现在只是拿着vue及uniapp的经验在一点一点的折腾,不过现在看来,鸿蒙入门并不是很难。也许是自己没有深入下去。 https://developer.harmonyos.com/cn/develop/deveco-studio#download…

docker使用详解

介绍 Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化。 Docker基于轻量级虚拟化技术,整个项目基于Go语言开…

Mybatis源码解析6:Mapper执行流程2-三个Handler

Mybatis源码解析6:Mapper执行流程2-三个Handler 1.项目结构2. 源码分析2.1 StatementHandler分析 BaseStatementHandler#prepare2.2 ParameterHandler分析 DefaultParameterHandler#setParameters2.3 ResultSetHandler分析 1.项目结构 2. 源码分析 之前已经对 Sim…

Scrapy爬虫学习

Scrapy爬虫学习一 1 scrapy框架1.1 scrapy 是什么1.2 安装scrapy 2 scrapy的使用2.1创建scrapy项目2.2 创建爬虫文件2.3爬虫文件的介绍2.4 运行爬虫文件 3 爬取当当网前十页数据3.1 dang.py:爬虫的主文件3.2 items.py 定义数据结构3.3 pipelines.py 管道3.4 执行命令…

总结了人工智能领域,能源领域,电气领域比较好中的一些sci期刊!!仅供参考

文章目录 前言一、总结了人工智能领域,能源领域,电气领域比较好中的一些sci期刊 总结 前言 期刊查询网站: https://www.letpub.com.cn/index.php?pagejournalapp&viewsearch 链接: 点我跳转期刊查询网站 一、总结了人工智能领域&#…

【Spring】02 Bean 的命名

文章目录 1. 定义2. 使用优势3. 如何命名4. 注解驱动5. 最佳实践1)使用明确的业务名词2)避免缩写和首字母缩略词2)不要过度使用别名 结语 在 Spring 框架中,Bean 是应用程序中的主要组件,负责承载和管理应用的核心功能…

【python-wrf】绘制wrf中的土地利用报错内容及其解决方法

从该代码处绘制wrf中的土地利用报错内容及其解决方法 1.报错内容: 微信公众平台 (qq.com)https://mp.weixin.qq.com/s/Cn0vhvfroVADPnT237LXNw --------------------------------------------------------------------------- AttributeError …

mysql 字符串合并方法以及合并为null问题

concat()不推荐 mysql一般提供了两种一种是concat()函数一种是concat_ws()函数,前者合并字符串有个弊端,合并字段不能有null值, 否则如下图合并后会是null concat_ws()推荐 concat_ws()函数可以解决合并字符串为null问题,conca…

使用Microsoft Dynamics AX 2012 - 8. 财务管理

财务管理的主要职责是控制和分析与货币金额有关的所有交易。这些事务发生在整个组织的业务流程中。 因此,财务管理是企业管理解决方案的核心领域。在Dynamics AX中,支持所有部门业务流程的应用程序的深度集成可立即提供准确的财务数据。 分类账交易的原…

MySQL 中Relay Log打满磁盘问题的排查方案

MySQL 中Relay Log打满磁盘问题的排查方案 引言: MySQL Relay Log(中继日志)是MySQL复制过程中的一个重要组件,它用于将主数据库的二进制日志事件传递给从数据库。然而,当中继日志不断增长并最终占满磁盘空间时&…

实操Nginx(4层代理+7层代理)+Tomcat多实例部署,实现负载均衡和动静分离

目录 前言 一、tomcat多实例部署 步骤一:先安装jdk,设置jdk的环境变量,验证是否安装完成(192.168.20.8) 步骤二:安装tomcat(192.168.20.18) 步骤三:安装tomcat多实例…

快速上手linux | 一文秒懂Linux各种常用目录命令(上)

🎬 鸽芷咕:个人主页 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活! 文章目录 一 、命令提示符和命令的基本格式1.1 如何查看主机名称及修改 二、命令基本格式2.1 命令格式示例2.2 参数的作用…

Spring Cloud gateway - CircuitBreaker GatewayFilte

前面学习Spring cloud gateway的时候,做测试的过程中我们发现,Spring Cloud Gateway不需要做多少配置就可以使用Spring Cloud LoadBalance的功能,比如: spring:application:name: spring-gatewaycloud:gateway:routes:- id: path…

ELK简单介绍二

学习目标 能够部署kibana并连接elasticsearch集群能够通过kibana查看elasticsearch索引信息知道用filebeat收集日志相对于logstash的优点能够安装filebeat能够使用filebeat收集日志并传输给logstash kibana kibana介绍 Kibana是一个开源的可视化平台,可以为ElasticSearch集群…

电子取证中Chrome各版本解密Cookies、LoginData账号密码、历史记录

文章目录 1.前置知识点2.对于80.X以前版本的解密拿masterkey的几种方法方法一 直接在目标机器运行Mimikatz提取方法二 转储lsass.exe 进程从内存提取masterkey方法三 导出SAM注册表 提取user hash 解密masterkey文件(有点麻烦不太推荐)方法四 已知用户密…

插入算法(C语言)

#include<cstdio> #include<iostream> #define N 9 using namespace std; int main() {int arr[N1] { 1,4,7,13,16,19,22,25,280 }; int in,i,j;//要插入的数字//打印要插入数字的数组所有元素printf("插入前的数组: ");for ( i 0; i <N; i){print…