零拷贝Zero copy-linux and java
什么是零拷贝
传统

零拷贝

背景-几种拷贝方式
方式1:Copying in Two Sample System Calls
read(file, tmp_buf, len);
write(socket, tmp_buf, len);

- 首先,调用read时,文件A copy到了kernel模式;
- 之后,CPU控制将kernel模式数据copy到user模式下;
- 调用write时,先将user模式下的内容copy到kernel模式下的socket的buffer中;
- 最后将kernel模式下的socket buffer的数据copy到网卡设备中传送;
方式2:Calling mmap
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

- 首先(通过DMA)将数据从磁盘读取到kernel buffer中;
- 然后将kernel buffer拷贝到socket buffer中;
- 最后将socket buffer中的数据copy到网卡设备(protocol engine)中发送;
其中mmap可以得到kernal buffer的引用,并直接进行使用
方式3:Replacing Read and Write with Sendfile
sendfile(socket, file, len);

- 首先(通过DMA)将数据从磁盘读取到kernel buffer中;
- 然后将kernel buffer拷贝到socket buffer中;
- 最后将socket buffer中的数据copy到网卡设备(protocol engine)中发送;
方式4:Hardware that supports gather can assemble data from multiple memory locations, eliminating another copy.
sendfile(socket, file, len);
调用无变化

- 将文件拷贝到kernel buffer中;
- 向socket buffer中追加当前要发生的数据在kernel buffer中的位置和偏移量;
- 根据socket buffer中的位置和偏移量直接将kernel buffer的数据copy到网卡设备(protocol engine)中;
经过上述过程,数据只经过了2次copy就从磁盘传送出去了。
linux零拷贝
Linux系统零拷贝调用
The Linux kernel supports zero-copy through various system calls, such as
sys/socket.h's sendfile, sendfile64
splice, tee, vmsplice
process_vm_readv, process_vm_writev
copy_file_range
raw sockets with packet mmap[4] or AF_XDP
sendfile()
splice()
splice() 系统调用和 sendfile() 非常类似,
用户应用程序必须拥有两个已经打开的文件描述符,一个用于表示输入设备,一个用于表示输出设备。与 sendfile() 不同的是,splice() 允许任意两个文件之间互相连接,而并不只是文件到 socket 进行数据传输。对于从一个文件描述符发送数据到 socket 这种特例来说,一直都是使用 sendfile() 这个系统调用,而 splice 一直以来就只是一种机制,它并不仅限于 sendfile() 的功能。
也就是说,sendfile() 只是 splice() 的一个子集,在 Linux 2.6.23 中,sendfile() 这种机制的实现已经没有了,但是这个 API 以及相应的功能还存在,只不过 API 以及相应的功能是利用了 splice() 这种机制来实现的。
总体来讲splice()是Linux 2.6.23 内核版本中替换sendfile()系统调用的一个方法,它不仅支持文件到Socket的直接传输,也支持文件到文件的直接传输I/O,但是其底层的传输过程和sendfile()并无区别。
零拷贝实现
- 最早的实现为IBM OS/360,其中一个程序可以指示通道子系统从一个文件或设备复制数据块到另一处,无需先转移数据。
- 实现零复制的软件通常依靠基于直接存储器访问(DMA)的复制,以及通过内存管理单元(MMU)的内存映射。这些功能需要特定硬件的支持,并通常涉及到特定存储器的对齐。
- 一种较新的方式为使用异构系统架构(HSA),便于CPU和GPU以及其他处理器传递指针。这需要CPU和GPU使用统一地址空间。
java 零拷贝
directbytebuffer and heapbytebuffer
directbytebuffer减少了堆大小,自己进行内存管理,可以避免gc引发的在不同内存区之间拷贝的问题
transferTo
private long transferToDirectly(long position, int icount,
WritableByteChannel target)
throws IOException
{
if (!transferSupported)
return IOStatus.UNSUPPORTED;
FileDescriptor targetFD = null;
if (target instanceof FileChannelImpl) {
if (!fileSupported)
return IOStatus.UNSUPPORTED_CASE;
targetFD = ((FileChannelImpl)target).fd;
} else if (target instanceof SelChImpl) {
// Direct transfer to pipe causes EINVAL on some configurations
if ((target instanceof SinkChannelImpl) && !pipeSupported)
return IOStatus.UNSUPPORTED_CASE;
targetFD = ((SelChImpl)target).getFD();
}
if (targetFD == null)
return IOStatus.UNSUPPORTED;
int thisFDVal = IOUtil.fdVal(fd);
int targetFDVal = IOUtil.fdVal(targetFD);
if (thisFDVal == targetFDVal) // Not supported on some configurations
return IOStatus.UNSUPPORTED;
long n = -1;
int ti = -1;
try {
begin();
ti = threads.add();
if (!isOpen())
return -1;
do {
n = transferTo0(thisFDVal, position, icount, targetFDVal);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
if (n == IOStatus.UNSUPPORTED_CASE) {
if (target instanceof SinkChannelImpl)
pipeSupported = false;
if (target instanceof FileChannelImpl)
fileSupported = false;
return IOStatus.UNSUPPORTED_CASE;
}
if (n == IOStatus.UNSUPPORTED) {
// Don't bother trying again
transferSupported = false;
return IOStatus.UNSUPPORTED;
}
return IOStatus.normalize(n);
} finally {
threads.remove(ti);
end (n > -1);
}
}
依赖于native方法
c实现:
https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/solaris/native/sun/nio/ch/FileChannelImpl.c
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
jint srcFD,
jlong position, jlong count,
jint dstFD)
{
#if defined(__linux__)
off64_t offset = (off64_t)position;
jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
if (n < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if ((errno == EINVAL) && ((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR) {
return IOS_INTERRUPTED;
}
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
return n;
#elif defined (__solaris__)
sendfilevec64_t sfv;
size_t numBytes = 0;
jlong result;
sfv.sfv_fd = srcFD;
sfv.sfv_flag = 0;
sfv.sfv_off = (off64_t)position;
sfv.sfv_len = count;
result = sendfilev64(dstFD, &sfv, 1, &numBytes);
/* Solaris sendfilev() will return -1 even if some bytes have been
* transferred, so we check numBytes first.
*/
if (numBytes > 0)
return numBytes;
if (result < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if (errno == EOPNOTSUPP)
return IOS_UNSUPPORTED_CASE;
if ((errno == EINVAL) && ((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR)
return IOS_INTERRUPTED;
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
return result;
#elif defined(__APPLE__)
off_t numBytes;
int result;
numBytes = count;
#ifdef __APPLE__
result = sendfile(srcFD, dstFD, position, &numBytes, NULL, 0);
#endif
if (numBytes > 0)
return numBytes;
if (result == -1) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if (errno == EOPNOTSUPP || errno == ENOTSOCK || errno == ENOTCONN)
return IOS_UNSUPPORTED_CASE;
if ((errno == EINVAL) && ((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR)
return IOS_INTERRUPTED;
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
return result;
#else
return IOS_UNSUPPORTED_CASE;
#endif
}
mmap
会有陆续的文章进行介绍
参考
https://www.linuxjournal.com/article/6345?page=0,0
https://blog.csdn.net/u013256816/article/details/52589524
https://en.wikipedia.org/wiki/Zero-copy
零拷贝Zero copy-linux and java的更多相关文章
- 零拷贝-zero copy
Efficient data transfer through zero copy Zero Copy I: User-Mode Perspective 0. 前言 在阅读RocketMQ的官方文档时 ...
- 对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解
此文章已同步发布在我的 segmentfault 专栏. 根据 Wiki 对 Zero-copy 的定义: "Zero-copy" describes computer opera ...
- 深入剖析Linux IO原理和几种零拷贝机制的实现
深入剖析Linux IO原理和几种零拷贝机制的实现 来源 https://zhuanlan.zhihu.com/p/83398714 零壹技术栈 公众号[零壹技术栈] 前言 零拷贝(Zero ...
- Linux、JDK、Netty中的NIO与零拷贝
一.先理解内核空间与用户空间 Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间,分别对应着下图中, CPU 特权等级分为4个,Linux 使用 Ring 0 和 Ring 3. 内核空 ...
- Linux 中的零拷贝技术,第 1 部分
概述 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.本文是本系列文章的第一部分,主要是介绍一些零拷贝技术的相关 ...
- Java基础-零拷贝技术应用案例
Java基础-零拷贝技术应用案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 零拷贝技术在Hadoop生态圈中很多组件得到应用,典型的比如kafka组件,它就很成功的应用了零拷贝 ...
- Netty 零拷贝(一)Linux 零拷贝
Netty 零拷贝(一)Linux 零拷贝 本文探讨 Linux 中主要的几种零拷贝技术以及零拷贝技术适用的场景. 一.几个重要的概念 1.1 用户空间与内核空间 操作系统的核心是内核,独立于普通的应 ...
- Linux文件和零拷贝
本文转载自文件和零拷贝 文件概述 文件描述符 文件描述符:在Linux中,所有的文件都是通过文件描述符引用.fd是一个非负整数.按照惯例,标准输入的fd是0,标准输出的fd是1,标准错误的fd是2.分 ...
- Linux 中的零拷贝技术,第 2 部分
技术实现 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.第一部分主要介绍了一些零拷贝技术的相关背景知识,简要概 ...
随机推荐
- MyBatis从入门到精通(十二):使用collection标签实现嵌套查询
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 本篇博客主要讲解使用collectio ...
- 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority queue)
堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...
- 盘一盘 System.out.println()
System.out.println("Hello World")是大部分程序员入门的第一行代码,也可以说是程序员们最熟悉的一行代码.大家真的深入研究过System.out.pri ...
- Java编程思想:简单的泛型
import java.util.ArrayList; import java.util.Random; public class Test { public static void main(Str ...
- TreeMap原理实现及常用方法
目录 一. TreeMap概述 二. 红黑树回顾 三. TreeMap构造 四. put方法 五. get 方法 六. remove方法 七. 遍历 八. 总结 前面我们分别讲了Map接口的两个实现类 ...
- GIL与异步回调
07.07自我总结 一.GIL 1.概念 在CPython中,这个全局解释器锁,也称为GIL,是一个互斥锁 2.带来的问题 首先必须明确执行一个py文件,分为三个步骤 从硬盘加载Python解释器到内 ...
- [小米OJ] 9. 移除 K 位得到最小值
思路: 重复k次: 1.找到并且删除第一个 num[i] > num[i+1] 的第i位数字. 2.若删除过程中,序列变成递增序列,则直接删除最后一位. 注意除去字符串头的0 def solut ...
- md文件的书写《一》
标题 :标题大小取决于#的多少 嵌套标题 使用 * - + 中的任一个加空格就可以实现创建列表 多层嵌套 我见青山多妩媚 (右边的尖括号加内容,实现引用) 这是第一段文字. 这是第二段文字. 段落以回 ...
- python红蓝英雄大乱斗(面向对象实现)
红蓝英雄大乱斗 游戏规则 ''' 有红蓝两方英雄(可自定义个数) 随机一方英雄使用随机攻击方式攻击另一方英雄,任意一方英雄全部阵亡则游戏结束 每个英雄有 名字.生命值.普通攻击.Q技能攻击.W技能攻击 ...
- python模块导入-软件开发目录规范-01
模块 模块的基本概念 模块: # 一系列功能的结合体 模块的三种来源 """ 模块的三种来源 1.python解释器内置的模块(os.sys....) 2.第三方的别人写 ...