什么是零拷贝

传统



零拷贝

背景-几种拷贝方式

方式1:Copying in Two Sample System Calls

read(file, tmp_buf, len);

write(socket, tmp_buf, len);

  1. 首先,调用read时,文件A copy到了kernel模式;
  2. 之后,CPU控制将kernel模式数据copy到user模式下;
  3. 调用write时,先将user模式下的内容copy到kernel模式下的socket的buffer中;
  4. 最后将kernel模式下的socket buffer的数据copy到网卡设备中传送;

方式2:Calling mmap

tmp_buf = mmap(file, len);

write(socket, tmp_buf, len);

  1. 首先(通过DMA)将数据从磁盘读取到kernel buffer中;
  2. 然后将kernel buffer拷贝到socket buffer中;
  3. 最后将socket buffer中的数据copy到网卡设备(protocol engine)中发送;

    其中mmap可以得到kernal buffer的引用,并直接进行使用

方式3:Replacing Read and Write with Sendfile

sendfile(socket, file, len);

  1. 首先(通过DMA)将数据从磁盘读取到kernel buffer中;
  2. 然后将kernel buffer拷贝到socket buffer中;
  3. 最后将socket buffer中的数据copy到网卡设备(protocol engine)中发送;

方式4:Hardware that supports gather can assemble data from multiple memory locations, eliminating another copy.

sendfile(socket, file, len);

调用无变化

  1. 将文件拷贝到kernel buffer中;
  2. 向socket buffer中追加当前要发生的数据在kernel buffer中的位置和偏移量;
  3. 根据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()并无区别。

零拷贝实现

  1. 最早的实现为IBM OS/360,其中一个程序可以指示通道子系统从一个文件或设备复制数据块到另一处,无需先转移数据。
  2. 实现零复制的软件通常依靠基于直接存储器访问(DMA)的复制,以及通过内存管理单元(MMU)的内存映射。这些功能需要特定硬件的支持,并通常涉及到特定存储器的对齐。
  3. 一种较新的方式为使用异构系统架构(HSA),便于CPU和GPU以及其他处理器传递指针。这需要CPU和GPU使用统一地址空间。

java 零拷贝

directbytebuffer and heapbytebuffer

directbytebuffer减少了堆大小,自己进行内存管理,可以避免gc引发的在不同内存区之间拷贝的问题

https://stackoverflow.com/questions/49174987/where-is-the-memory-of-direct-buffer-allocated-by-netty-kernel-space-or-user-sp

transferTo

java实现:

https://github.com/openjdk-mirror/jdk7u-jdk/blob/f4d80957e89a19a29bb9f9807d2a28351ed7f7df/src/share/classes/sun/nio/ch/FileChannelImpl.java

  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的更多相关文章

  1. 零拷贝-zero copy

    Efficient data transfer through zero copy Zero Copy I: User-Mode Perspective 0. 前言 在阅读RocketMQ的官方文档时 ...

  2. 对于 Netty ByteBuf 的零拷贝(Zero Copy) 的理解

    此文章已同步发布在我的 segmentfault 专栏. 根据 Wiki 对 Zero-copy 的定义: "Zero-copy" describes computer opera ...

  3. 深入剖析Linux IO原理和几种零拷贝机制的实现

    深入剖析Linux IO原理和几种零拷贝机制的实现 来源 https://zhuanlan.zhihu.com/p/83398714 零壹技术栈      公众号[零壹技术栈] 前言 零拷贝(Zero ...

  4. Linux、JDK、Netty中的NIO与零拷贝

    一.先理解内核空间与用户空间 Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间,分别对应着下图中, CPU 特权等级分为4个,Linux 使用 Ring 0 和 Ring 3. 内核空 ...

  5. Linux 中的零拷贝技术,第 1 部分

    概述 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.本文是本系列文章的第一部分,主要是介绍一些零拷贝技术的相关 ...

  6. Java基础-零拷贝技术应用案例

    Java基础-零拷贝技术应用案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 零拷贝技术在Hadoop生态圈中很多组件得到应用,典型的比如kafka组件,它就很成功的应用了零拷贝 ...

  7. Netty 零拷贝(一)Linux 零拷贝

    Netty 零拷贝(一)Linux 零拷贝 本文探讨 Linux 中主要的几种零拷贝技术以及零拷贝技术适用的场景. 一.几个重要的概念 1.1 用户空间与内核空间 操作系统的核心是内核,独立于普通的应 ...

  8. Linux文件和零拷贝

    本文转载自文件和零拷贝 文件概述 文件描述符 文件描述符:在Linux中,所有的文件都是通过文件描述符引用.fd是一个非负整数.按照惯例,标准输入的fd是0,标准输出的fd是1,标准错误的fd是2.分 ...

  9. Linux 中的零拷贝技术,第 2 部分

    技术实现 本系列由两篇文章组成,介绍了当前用于 Linux 操作系统上的几种零拷贝技术,简单描述了各种零拷贝技术的实现,以及它们的特点和适用场景.第一部分主要介绍了一些零拷贝技术的相关背景知识,简要概 ...

随机推荐

  1. Centos7 下nginx 搭建文件图片服务器

    现在服务器部署nginx yum install -y epel-release yum install nginx -y 安装完成之后 访问ip 由此可见nginx服务是可用的 修改nginx的配置 ...

  2. 后端开发工具:反编译工具、VS插件、.NET Framework源码地址

    再学习.工作中,开发免不了要使用第三方工具.今天介绍2款反编译工具 一.dnspy 免安装.免费.可调试.可修改重新编译dll 开源项目地址:https://github.com/0xd4d/dnSp ...

  3. @ImportResource导入的xml配置里的Bean能够使用@PropertySource导入的属性值吗?

    每篇一句 大师都是偏执的,偏执才能产生力量,妥协是没有力量的.你对全世界妥协了你就是空气.所以若没有偏见,哪来的大师呢 相关阅读 [小家Spring]详解PropertyPlaceholderConf ...

  4. [记录]Shell并发模式批量安装saltstack的脚本

    SaltStack+Shell: salt-master的配置: #cat /etc/salt/master user: root auto_accept: True salt-minion的配置(支 ...

  5. R035---偷个懒,用UiPath录制电脑操作过程,迅速实现流程自动化

    ​一.缘起 UiPath可以录制你操作电脑的过程,从而实现自动化. 目前有点鸡肋,因为有些操作过程无法录制,例如: 键盘快捷键 修改键 右键点击 鼠标悬停 即便如此,录制功能有时候还是可以用一下,特别 ...

  6. 默认文档接卸--手机web app开发笔记(二)

    首先我们启动HBuilderX2.0 ,界面如图2-1所示 图2-1 软件开发界面 单击“文件—新建—项目”,弹出新建项目管理界面,我们在里面进行了项目类型选择“5+APP”.项目名称填写“编程之路” ...

  7. 洛谷 P4363 [九省联考2018]一双木棋chess 题解

    题目链接:https://www.luogu.org/problemnew/show/P4363 分析: 首先博弈,然后考虑棋盘的规则,因为一个子在落下时它的上面和左面都已经没有空位了,所以棋子的右下 ...

  8. android实现倒计时,最简单实现RecyclerView倒计时+SwipeRefreshLayout下拉刷新

    先上效果图: RecyclerView + SwipeRefreshLayout 实现倒计时效果 MainActivity.java package top.wintp.counttimedemo1; ...

  9. 【转】8年!我在OpenStack路上走过的坑。。。

    8年!我在OpenStack路上走过的坑... 摘要: 2010年10月,OpenStack发布了第一个版本:上个月,发布了它的第18个版本Rocky.几年前气氛火爆,如今却冷冷清清.Rocky版本宣 ...

  10. web设计_4_可扩展的行

    不要指定横向页面组件的高度,要让它们能够在纵向自由扩展. 常见的包含文章正文或大段文字的区域,应该适应任何篇幅和大小的文字. 但是例如文章标题.登陆信息栏等也要考虑文字内容数量及高度的变化. 例如:下 ...