了解Linux 和 Java 的零拷贝
Linux传统IO
大家好,我是一段躺在Linux磁盘上的数据。现在要把我从磁盘发到网卡,需要经过以下步骤:
读操作
如上图:操作系统把内存分为了内核空间和用户空间。首先位于用户空间的应用程序使用发起数据读操作,比如JVM发起read()
系统调用。这个时候操作系统会进行一次上下文切换:从用户空间切换到内核空间。
然后内核空间通知磁盘,内核把我从磁盘copy到内核缓冲区。这个过程是由一个叫“DMA(Direct memory access)”的硬件来做的,所以不需要CPU的参与。
然后内核把我从内核缓冲区copy到应用程序缓冲区,这里需要CPU的参与。
最后进行上下文切换,又换回到用户空间的上下文。
整个读操作的过程需要两次上下文切换和两次copy。
相关学习推荐:Java视频教程
写操作
写操作与读操作类似,只是方向相反而已,仍然需要两次上下文切换和两次数据的copy。我可能会被写到磁盘,也可能会被写到网卡。
内存映射
从上面的过程可以看到,如果想把我从磁盘发送到网卡,需要总共4次上下文切换和4次copy操作。我被操作系统在内核空间和用户空间之间来回复制,但其实我在这期间什么也没有做,什么也没有变化,就是复制而已,所以这个IO模型太浪费操作系统资源了,我被复制这么多次,身心疲惫。而且操作系统的资源是非常宝贵滴~
现在主流的操作系统都使用了虚拟内存。简单来说,就是用虚拟地址取代物理地址,这样做可以让多个虚拟内存只想同一个物理地址,虚拟内存的空间可以远远大于物理内存的空间。
那如果操作系统能够把用户空间的应用程序缓冲区和内核空间的内核缓冲区映射到同一个物理地址,那岂不是就少了很多复制的过程?如下图:
Linux零拷贝
所以为了解决这个问题,聪明的Linux开发者们写了一些新的系统调用来做这个事。主要有两种方式:
- mmap + write
- sendfile
mmap + write
mmap()
系统调用首先会使用DMA copy的方式将我从磁盘读取到内核缓冲区,然后通过内存映射的方式,使用户缓冲区和内核读缓冲区的内存地址为同一内存地址,也就是说,不需要CPU再将我从内核读缓冲区复制到用户缓冲区啦!
当使用write()
系统调用的时候,CPU将我从内核缓冲区(等同于用户缓冲区)直接写入到需要发送的内核缓冲区,比如网络发送缓冲区(socket buffer),然后通过DMA的方式将我传入到网卡驱动程序(或磁盘)中准备发送。
mmap + write的方式读写数据总共需要两次系统调用,4次上下文切换,2次DMA Copy和1次CPU Copy。
sendfile
sendfile也是一个系统调用,它其实本质上就是把上述两个系统调用的功能合起来,变成了一个调用。这样做的好处是,操作系统只需要2次上下文切换了,减少了2次上下文切换的开销。
gather
Linux2.4内核对sendfile进行了优化,提供了gather操作,这个操作可以把上图中的最后一次CPU copy去掉,原理就是不复制数据,而是把数据在之前的内核缓冲区(比如图中的案例是Read Buffer)的内存地址、偏移量记录发送给目标内核缓冲区(比如图中案例的Socket Buffer),这样在最后的DMA copy阶段就可以拿着这个指针直接去找数据copy了。
Java NIO使用零拷贝
Linux的零拷贝确实能够节约一些操作系统的资源。所以Java的NIO为了支持零拷贝,提供了一些类:
- DirectByteBuffer
- FileChannel
在之前的《Java NIO - Buffer》这篇文章里大概介绍了DirectByteBuffer。ByteBuffer主要有两种实现,一种是DirectByteBuffer, 一种是HeapByteBuffer。
其中,DirectByteBuffer直接在堆外分配内存,底层是直接通过JNI调用操作系统的NIO系统调用,所以性能会比较高。而HeapByteBuffer是堆内内存,而且数据需要多一次拷贝,所以性能比较低。
FileChannel
是Java NIO提供的用于复制文件的类,可以把文件复制到磁盘或者网络等。
map
方法其实就是采用了操作系统中的内存映射方式,将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。
transferTo
方法直接将当前通道内容传输到另一个通道,也就是说这种方式不会有内核缓冲区到用户缓冲区的读写问题。底层是sendfile系统调用。transferFrom
方法同理。
示例代码:
File file = new File("test.txt");RandomAccessFile raf = new RandomAccessFile(file, "rw");FileChannel fileChannel = raf.getChannel();SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("", 8080));// 直接使用了transferTo()进行通道间的数据传输fileChannel.transferTo(0, fileChannel.size(), socketChannel);
作者:公众号_xy的技术圈
链接:www.imooc.com/article/289550
来源:慕课网
以上内容来自幕课网
零拷贝的再次理解
零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有 kernel buffer 有一份数据)。
零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。
mmap和sendFile的区别
mmap 适合小数据量读写,sendFile 适合大文件传输。
mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。
sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)。
以上是了解Linux 和 Java 的零拷贝的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

PHP是一种广泛应用于服务器端的脚本语言,特别适合web开发。1.PHP可以嵌入HTML,处理HTTP请求和响应,支持多种数据库。2.PHP用于生成动态网页内容,处理表单数据,访问数据库等,具有强大的社区支持和开源资源。3.PHP是解释型语言,执行过程包括词法分析、语法分析、编译和执行。4.PHP可以与MySQL结合用于用户注册系统等高级应用。5.调试PHP时,可使用error_reporting()和var_dump()等函数。6.优化PHP代码可通过缓存机制、优化数据库查询和使用内置函数。7

PHP和Python各有优势,适合不同场景。1.PHP适用于web开发,提供内置web服务器和丰富函数库。2.Python适合数据科学和机器学习,语法简洁且有强大标准库。选择时应根据项目需求决定。

PHP适合web开发,特别是在快速开发和处理动态内容方面表现出色,但不擅长数据科学和企业级应用。与Python相比,PHP在web开发中更具优势,但在数据科学领域不如Python;与Java相比,PHP在企业级应用中表现较差,但在web开发中更灵活;与JavaScript相比,PHP在后端开发中更简洁,但在前端开发中不如JavaScript。

PHP成为许多网站首选技术栈的原因包括其易用性、强大社区支持和广泛应用。1)易于学习和使用,适合初学者。2)拥有庞大的开发者社区,资源丰富。3)广泛应用于WordPress、Drupal等平台。4)与Web服务器紧密集成,简化开发部署。

启动 Apache 的步骤如下:安装 Apache(命令:sudo apt-get install apache2 或从官网下载)启动 Apache(Linux:sudo systemctl start apache2;Windows:右键“Apache2.4”服务并选择“启动”)检查是否已启动(Linux:sudo systemctl status apache2;Windows:查看服务管理器中“Apache2.4”服务的状态)启用开机自动启动(可选,Linux:sudo systemctl

当 Apache 80 端口被占用时,解决方法如下:找出占用该端口的进程并关闭它。检查防火墙设置以确保 Apache 未被阻止。如果以上方法无效,请重新配置 Apache 使用不同的端口。重启 Apache 服务。

本文介绍如何在Debian系统上有效监控Nginx服务器的SSL性能。我们将使用NginxExporter将Nginx状态数据导出到Prometheus,再通过Grafana进行可视化展示。第一步:配置Nginx首先,我们需要在Nginx配置文件中启用stub_status模块来获取Nginx的状态信息。在你的Nginx配置文件(通常位于/etc/nginx/nginx.conf或其包含文件中)中添加以下代码段:location/nginx_status{stub_status

本文介绍两种在Debian系统中配置回收站的方法:图形界面和命令行。方法一:使用Nautilus图形界面打开文件管理器:在桌面或应用程序菜单中找到并启动Nautilus文件管理器(通常名为“文件”)。找到回收站:在左侧导航栏中寻找“回收站”文件夹。如果找不到,请尝试点击“其他位置”或“计算机”进行搜索。配置回收站属性:右键点击“回收站”,选择“属性”。在属性窗口中,您可以调整以下设置:最大大小:限制回收站可用的磁盘空间。保留时间:设置文件在回收站中自动删除前的保
