框架篇:Linux零拷贝机制和FileChannel
前言
大白话解释,零拷贝就是没有把数据从一个存储区域拷贝到另一个存储区域。但是没有数据的复制,怎么可能实现数据的传输呢?其实我们在java NIO、netty、kafka遇到的零拷贝,并不是不复制数据,而是减少不必要的数据拷贝次数,从而提升代码性能
- 零拷贝的好处
- 内核空间和用户空间
- 缓冲区和虚拟内存
- 传统的 I/O
- mmap+write 实现的零拷贝
- sendfile 实现的零拷贝
- 带有DMA收集拷贝功能的sendfile实现的零拷贝
- java提供的零拷贝方式
关注公众号,一起交流 :潜行前行
零拷贝的好处
- 减少或避免不必要的CPU数据拷贝,从而释放CPU去执行其他任务
- 零拷贝机制能减少用户空间和操作系统内核空间的上下文切换
- 减少内存的占用
内核空间和用户空间
- 内核空间:Linux自身使用的空间;主要提供进程调度、内存分配、连接硬件资源等功能
- 用户空间:提供给各个程序进程的空间;用户空间不具有访问内核空间资源的权限,如果应用程序需要使用到内核空间的资源,则需要通过系统调用来完成:从用户空间切换到内核空间,完成相关操作后再从内核空间切换回用户空间
缓冲区和虚拟内存
- 直接内存访问(Direct Memory Access)(DMA)
- 直接内存访问:DMA允许外设设备和内存存储器之间直接进行IO数据传输,其过程不需要CPU的参与
- 缓冲区 是所有I/O的基础,I/O 无非就是把数据移进或移出缓冲区
- 进程发起read请求,内核先检查内核空间缓冲区是否存在进程所需数据,如果已经存在,则直接copy数据到进程的内存区。如果没有,系统则向磁盘请求数据,通过DMA写入内核的read缓冲冲区,接着再将内核缓冲区数据copy到进程的内存区
- 进程发起write请求,则是把进程的内存区数据copy到内核的write缓冲区,然后再通过DMA把内核缓冲区数据刷回磁盘或者网卡中
- 虚拟内存:现代操作系统都使用虚拟内存,有如下两个好处
- 一个以上的虚拟地址可以指向同一个物理内存地址
- 虚拟内存空间可大于实际可用的物理地址
- 利用第一点特性可以把内核空间地址和用户空间的虚拟地址映射到同一个物理地址,这样DMA就可以填充(读写)对内核和用户空间进程同时可见的缓冲区了;大致如下
传统的 I/O
#include <unistd>
ssize_t write(int filedes, void *buf, size_t nbytes);
ssize_t read(int filedes, void *buf, size_t nbytes);
- 如java在linux系统上,读取一个磁盘文件,并发送到远程端的服务
- 1)发出read系统调用,会导致用户空间到内核空间的上下文切换,然后再通过DMA将文件中的数据从磁盘上读取到内核空间缓冲区
- 2)接着将内核空间缓冲区的数据拷贝到用户空间进程内存,然后read系统调用返回。而系统调用的返回又会导致一次内核空间到用户空间的上下文切换
- 3)write系统调用,则再次导致用户空间到内核空间的上下文切换,将用户空间的进程里的内存数据复制到内核空间的socket缓冲区(也是内核缓冲区,不过是给socket使用的),然后write系统调用返回,再次触发上下文切换
- 4)至于socket缓冲区到网卡的数据传输则是独立异步的过程,也就是说write系统调用的返回并不保证数据被传输到网卡
一共有四次用户空间与内核空间的上下文切换。四次数据copy,分别是两次CPU数据复制,两次DMA数据复制
mmap+write实现的零拷贝
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
- 1)发出mmap系统调用,导致用户空间到内核空间的上下文切换。然后通过DMA引擎将磁盘文件中的数据复制到内核空间缓冲区
- 2)mmap系统调用返回,导致内核空间到用户空间的上下文切换
- 3)这里不需要将数据从内核空间复制到用户空间,因为用户空间和内核空间共享了这个缓冲区
- 4)发出write系统调用,导致用户空间到内核空间的上下文切换。将数据从内核空间缓冲区复制到内核空间socket缓冲区;write系统调用返回,导致内核空间到用户空间的上下文切换
- 5)异步,DMA引擎将socket缓冲区中的数据copy到网卡
通过mmap实现的零拷贝I/O进行了4次用户空间与内核空间的上下文切换,以及3次数据拷贝;其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝
sendfile实现的零拷贝
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
- 1)发出sendfile系统调用,导致用户空间到内核空间的上下文切换,然后通过DMA引擎将磁盘文件中的内容复制到内核空间缓冲区中,接着再将数据从内核空间缓冲区复制到socket相关的缓冲区
- 2)sendfile系统调用返回,导致内核空间到用户空间的上下文切换。DMA异步将内核空间socket缓冲区中的数据传递到网卡
通过sendfile实现的零拷贝I/O使用了2次用户空间与内核空间的上下文切换,以及3次数据的拷贝。其中3次数据拷贝中包括了2次DMA拷贝和1次CPU拷贝
带有DMA收集拷贝功能的sendfile实现的零拷贝
- 从Linux 2.4版本开始,操作系统提供scatter和gather的SG-DMA方式,直接从内核空间缓冲区中将数据读取到网卡,无需将内核空间缓冲区的数据再复制一份到socket缓冲区
- 1)发出sendfile系统调用,导致用户空间到内核空间的上下文切换。通过DMA引擎将磁盘文件中的内容复制到内核空间缓冲区
- 2)这里没把数据复制到socket缓冲区;取而代之的是,相应的描述符信息被复制到socket缓冲区。该描述符包含了两种的信息:A)内核缓冲区的内存地址、B)内核缓冲区的偏移量
- 3)sendfile系统调用返回,导致内核空间到用户空间的上下文切换。DMA根据socket缓冲区的描述符提供的地址和偏移量直接将内核缓冲区中的数据复制到网卡
带有DMA收集拷贝功能的sendfile实现的I/O使用了2次用户空间与内核空间的上下文切换,以及2次数据的拷贝,而且这2次的数据拷贝都是非CPU拷贝。这样一来我们就实现了最理想的零拷贝I/O传输了,不需要任何一次的CPU拷贝,以及最少的上下文切换
java提供的零拷贝方式
- java NIO的零拷贝实现是基于mmap+write方式
- FileChannel的map方法产生的MappedByteBuffer
FileChannel提供了map()方法,该方法可以在一个打开的文件和MappedByteBuffer之间建立一个虚拟内存映射,MappedByteBuffer继承于ByteBuffer;该缓冲器的内存是一个文件的内存映射区域。map方法底层是通过mmap实现的,因此将文件内存从磁盘读取到内核缓冲区后,用户空间和内核空间共享该缓冲区。用法如下
public void main(String[] args){
try {
FileChannel readChannel = FileChannel.open(Paths.get("./cscw.txt"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
MappedByteBuffer data = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
//数据传输
writeChannel.write(data);
readChannel.close();
writeChannel.close();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
- FileChannel的transferTo、transferFrom
如果操作系统底层支持的话,transferTo、transferFrom也会使用相关的零拷贝技术来实现数据的传输。用法如下
public void main(String[] args) {
try {
FileChannel readChannel = FileChannel.open(Paths.get("./cscw.txt"), StandardOpenOption.READ);
FileChannel writeChannel = FileChannel.open(Paths.get("./siting.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
long len = readChannel.size();
long position = readChannel.position();
//数据传输
readChannel.transferTo(position, len, writeChannel);
//效果和transferTo 一样的
//writeChannel.transferFrom(readChannel, position, len, );
readChannel.close();
writeChannel.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
欢迎指正文中错误
关注公众号,一起交流
参考文章
框架篇:Linux零拷贝机制和FileChannel的更多相关文章
- 深入剖析Linux IO原理和几种零拷贝机制的实现
深入剖析Linux IO原理和几种零拷贝机制的实现 来源 https://zhuanlan.zhihu.com/p/83398714 零壹技术栈 公众号[零壹技术栈] 前言 零拷贝(Zero ...
- Netty源码解析 -- 零拷贝机制与ByteBuf
本文来分享Netty中的零拷贝机制以及内存缓冲区ByteBuf的实现. 源码分析基于Netty 4.1.52 Netty中的零拷贝 Netty中零拷贝机制主要有以下几种 1.文件传输类DefaultF ...
- Linux零拷贝技术
本文转载自Linux零拷贝技术 导语 本文讲解 Linux 的零拷贝技术,云计算是一门很庞大的技术学科,融合了很多技术,Linux 算是比较基础的技术,所以,学好 Linux 对于云计算的学习会有比较 ...
- Linux零拷贝原理
Linux零拷贝原理 前言 磁盘可以说是计算机系统最慢的硬件之一,读写速度相差内存 10 倍以上,所以针对优化磁盘的技术非常的多,比如零拷贝.直接 I/O.异步 I/O 等等,这些优化的目的就是为了提 ...
- Netty 零拷贝(一)Linux 零拷贝
Netty 零拷贝(一)Linux 零拷贝 本文探讨 Linux 中主要的几种零拷贝技术以及零拷贝技术适用的场景. 一.几个重要的概念 1.1 用户空间与内核空间 操作系统的核心是内核,独立于普通的应 ...
- Linux零拷贝技术 直接 io
Linux零拷贝技术 .https://kknews.cc/code/2yeazxe.html https://zhuanlan.zhihu.com/p/76640160 https://clou ...
- NIO学习笔记,从Linux IO演化模型到Netty—— Linux零拷贝
这里只是感性地认识Linux零拷贝,不涉及具体细节. 1.Linux传统的数据拷贝 用户进程是不能直接访问文件系统的,要先切换到内核态,发起系统调用,DMA把磁盘中的数据写入内核空间,内核再把数据拷贝 ...
- Linux零拷贝技术,看完这篇文章就懂了
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫. 本文讲解 ...
- Linux 零拷贝技术
简介 零拷贝(zero-copy)技术可以减少数据拷贝和共享总线操作的次数,消除通信数据在存储器之间不必要的中间拷贝过程,有效地提高通信效率,是设计高速接口通道.实现高速服务器和路由器的关键技术之一. ...
随机推荐
- URL地址中传递数组参数的方法
示例:http://127.0.0.1/text/index.php?links[]=ddddd&links[]=fffff var_dump($_GET); array(1) { [ ...
- Laravel Model查询结果的3种存储格式内存占用对比
PHP Laravel框架支持Model查询数据后可以有多种方式返回数据,对新手会造成一些困扰,比如数组Model对象.集合.纯数组 今天从内存占用的角度对比一下3种数据返回方式 按数组Model对象 ...
- Apache Hudi和Presto的前世今生
一篇由Apache Hudi PMC Bhavani Sudha Saktheeswaran和AWS Presto团队工程师Brandon Scheller分享Apache Hudi和Presto集成 ...
- redis集群简介
1.1 集群的概念 所谓的集群,就是通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定.高效的状态. 1.1.1 使用redis集群的必要性 问题:我们已经部署好 ...
- 《Redis入门指南》笔记
第1章 简介 1.1 历史与发展 2008年 意大利创业公司创始人因对mysql性能不满意,于是他决定开发redis. 2009年 redis初版由他一个人开发完成.redis是"remot ...
- C++获取运行程序当前目录
1 HMODULE GetSelfModuleHandle() 2 { 3 MEMORY_BASIC_INFORMATION mbi; 4 return ((::VirtualQuery(GetSel ...
- 国产化之路-安装WEB服务器
专题目录 国产化之路-统信UOS操作系统安装 国产化之路-国产操作系统安装.net core 3.1 sdk 国产化之路-安装WEB服务器 国产化之路-安装达梦DM8数据库 国产化之路-统信UOS + ...
- C++实现职工管理系统(上)
C++实现职工管理系统(上) 大家好呀,时间过得真快,在博客园已经第七天了,博主今天给大家带来的是职工管理系统(C++)(上) 这次的随笔记录的是实现职工管理系统所需要的类 目录 C++实现职工管理系 ...
- lamda表达式与Stream 流操作,reduce,flatMap,groupingBy等
/** * 符合lambda表达式的接口也叫函数式接口: * 除了默认方法和Object类的方法外,只有一个抽象方法的接口才能符合lambda表达式的要求 * 可以使用@FunctionalInter ...
- C语言&C++ 中External dependencies
参考:https://blog.csdn.net/yyyzlf/article/details/4419593 External Dependencies是说你没有把这个文件加入到这个工程中,但是 ...