零拷贝这三个字,一直是服务器网络编程的关键字,任何性能优化都离不开。在 Java 程序员的世界,常用的零拷贝有 mmap 和 sendFile。那么,他们在 OS 里,到底是怎么样的一个的设计?本文将简单聊聊 mmap 和 sendFile 这两个零拷贝。

一、传统IO的劣势

初学 Java 时,我们在学习 IO 和 网络编程时,会使用以下代码:

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

我们会调用 read 方法读取 index.html 的内容—— 变成字节数组,然后调用 write 方法,将 index.html 字节流写到 socket 中,那么,我们调用这两个方法,在 OS 底层发生了什么呢?我这里借鉴了一张其他文章的图片,尝试解释这个过程。

上图中,上半部分表示用户态和内核态的上下文切换。下半部分表示数据复制操作。下面说说他们的步骤:

  1. read 调用导致用户态到内核态的一次变化,同时,第一次复制开始:DMA(Direct Memory Access,直接内存存取,即不使用 CPU 拷贝数据到内存,而是 DMA 引擎传输数据到内存,用于解放 CPU) 引擎从磁盘读取 index.html 文件,并将数据放入到内核缓冲区。

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

  3. 发生第三次数据拷贝,我们调用 write 方法,系统将用户缓冲区的数据拷贝到 Socket 缓冲区。此时,又发生了一次用户态到内核态的上下文切换。

  4. 第四次拷贝,数据异步的从 Socket 缓冲区,使用 DMA 引擎拷贝到网络协议引擎。这一段,不需要进行上下文切换。

  5. write 方法返回,再次从内核态切换到用户态。

如你所见,复制拷贝操作太多了。如何优化这些流程?

二、mmap 优化

mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户空间的拷贝次数。如下图:

如上图,user buffer 和 kernel buffer 共享 index.html。如果你想把硬盘的 index.html 传输到网络中,再也不用拷贝到用户空间,再从用户空间拷贝到 Socket 缓冲区。

现在,你只需要从内核缓冲区拷贝到 Socket 缓冲区即可,这将减少一次内存拷贝(从 4 次变成了 3 次),但不减少上下文切换次数。

三、sendFile

那么,我们还能继续优化吗? Linux 2.1 版本 提供了 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换。

如上图,我们进行 sendFile 系统调用时,数据被 DMA 引擎从文件复制到内核缓冲区,然后调用 write 方法时,从内核缓冲区进入到 Socket,这时,是没有上下文切换的,因为都在内核空间。

最后,数据从 Socket 缓冲区进入到协议栈。此时,数据经过了 3 次拷贝,3 次上下文切换。那么,还能不能再继续优化呢? 例如直接从内核缓冲区拷贝到网络协议栈?

实际上,Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket buffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。具体如下图:

现在,index.html 要从文件进入到网络协议栈,只需 2 次拷贝:第一次使用 DMA 引擎从文件拷贝到内核缓冲区,第二次从内核缓冲区将数据拷贝到网络协议栈;内核缓存区只会拷贝一些 offset 和 length 信息到 SocketBuffer,基本无消耗。

等一下,不是说零拷贝吗?为什么还是要 2 次拷贝?

首先我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有 kernel buffer 有一份数据,sendFile 2.1 版本实际上有 2 份数据,算不上零拷贝)。例如我们刚开始的例子,内核缓存区和 Socket 缓冲区的数据就是重复的。

而零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。

再稍微讲讲 mmap 和 sendFile 的区别。

  1. mmap 适合小数据量读写,sendFile 适合大文件传输。
  2. mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最少 2 次数据拷贝。
  3. sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝到 Socket 缓冲区)。

在这个选择上:rocketMQ 在消费消息时,使用了 mmap。kafka 使用了 sendFile。

四、Java中的例子

kafka 在客户端和 broker 进行数据传输时,会使用 transferTo 和 transferFrom 方法,即对应 Linux 的 sendFile。

tomcat 内部在进行文件拷贝的时候,也会使用 transferto 方法。

tomcat 在处理一下心跳保活时,也会调用该 sendFile 方法。

所以,如果你需要优化网络传输的性能,或者文件读写的速度,请尽量使用零拷贝。它不仅能较少复制拷贝次数,还能较少上下文切换,缓存行污染。

什么是零拷贝?mmap与sendFile的区别是什么?相关推荐

  1. 消息中间件零拷贝?mmap与sendFile的区别是什么?

    什么是零拷贝? 在这个方法里面,我们没有在内存层面去"复制(Copy)"数据,所以这个方法,也被称之为零拷贝(Zero-Copy).IBM Developer Works里面有一篇 ...

  2. 什么是mmap?零拷贝?DMA?

    <Linux Zero-copy零拷贝技术全面揭秘> <什么是mmap?零拷贝?DMA?> <Linux C语言:用零拷贝技术实现TCP代理(源代码+测试服务端客户端代码 ...

  3. 零拷贝、mmap、sendfile

    目录 零拷贝 mmap sendFile 总结 零拷贝 要了解零拷贝,首先得先了解一下传统 IO 的执行流程,这里举个例子,通过传统的 IO 进行网络传输来传输一个文件. 先上一张图,这张图就代表了传 ...

  4. Java 两种zero-copy零拷贝技术mmap和sendfile的介绍

    详细介绍了两种zero-copy零拷贝技术mmap和sendfile的概念和基本原理. 文章目录 1 标准IO 2 零拷贝 2.1 sendfile调用 2.1 mmap调用 2.2 MQ中的应用 1 ...

  5. Netty、Kafka中的零拷贝技术到底有多牛?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:rrd.me/ggFBd 零拷贝,从字面意思理解就是数据不需 ...

  6. 深入探秘 Netty、Kafka 中的零拷贝技术!

    作者:ksfzhaohui 原文:juejin.im/post/5cad6f1ef265da039f0ef5df 零拷贝,从字面意思理解就是数据不需要来回的拷贝,大大提升了系统的性能.我们也经常在 J ...

  7. python epoll多路复用技术_python:多路复用+零拷贝

    作为通信模块目前比较热的2个词:零拷贝,多路复用,都是性能提升较多的词,发送方利用零拷贝技术减少内存拷贝的时空开销,提升性能,接收方利用I/O多路复用技术,加速数据接受. 零拷贝 一种高效的数据传输机 ...

  8. 零拷贝 zero-copy 原理

    引言 传统的 Linux 操作系统的标准 I/O 接口是基于数据拷贝操作的,即 I/O 操作会导致数据在操作系统内核地址空间的缓冲区和应用程序地址空间定义的缓冲区之间进行传输.这样做最大的好处是可以减 ...

  9. 关于零拷贝的一点认识

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | ksfzhaohui 来源 | https:/ ...

最新文章

  1. 单相计量芯片RN8209D使用经验分享(转)
  2. 正睿 2018 提高组十连测 Day4 T3 碳
  3. 《转载》Python并发编程之线程池/进程池--concurrent.futures模块
  4. 保证java的jar包在后台运行
  5. 区块链预言机(4)内在机制
  6. Nginx服务的命令行控制
  7. c语言二级考试真题新疆,新疆自治区计算机二级C语言考试题库.doc
  8. 在Red Hat 4 AS U7上安装oracle10gR2
  9. XAMPP on Mac 组态 Virual Host
  10. python修改mac地址_python利用_winreg模块制作MAC地址修改工具
  11. linux 如何查看进程内用户虚拟空间分布
  12. linux sudo提权
  13. boot定时任务开启和关闭 spring_spring-boot 多线程并发定时任务的解决方案
  14. 电脑键盘上每个键的作用_键盘F键有什么作用 键盘F键作用介绍【详解】
  15. 【To Understand】动态规划:求最长公共子串/最长公共子序列
  16. 键盘鼠标录制哪个好用_美商海盗船Scimitar RGB Elite鼠标体验:再多技能也怕这把弯刀...
  17. 大数据行业发展前景及岗位方向如何?
  18. manjaro踩坑记
  19. Raspberry Pi 上手准备
  20. 爬点今日头条街拍美女。。。

热门文章

  1. 你了解现在的招聘网站吗?
  2. sql 去除重复之distinct详解
  3. java 三种工厂模式(简单工厂+工厂方法+抽象工厂)
  4. javaScript基础之隐式转换
  5. java操作win10防火墙
  6. 修改和固定局域网的IP地址
  7. 列表嵌套的元组改成列表嵌套列表
  8. 【C语言典例】——day1:阶梯问题(条件判断)
  9. STM32重写fputc
  10. 【父亲节】各种语言的父亲节祝福