文档章节

JAVA Zero Copy的相关知识

cloud-coder
 cloud-coder
发布于 2014/08/11 11:06
字数 1554
阅读 4002
收藏 19
点赞 1
评论 0

介绍

     java 的zero copy多在网络应用程序中使用。Java的libaries在linux和unix中支持zero copy,关键的api是java.nio.channel.FileChannel的transferTo(),transferFrom()方法。我们可以用这两个方法来把bytes直接从调用它的channel传输到另一个writable byte channel,中间不会使data经过应用程序,以便提高数据转移的效率。

    许多web应用都会向用户提供大量的静态内容,这意味着有很多data从硬盘读出之后,会原封不动的通过socket传输给用户。这种操作看起来可能不会怎么消耗CPU,但是实际上它是低效的:kernal把数据从disk读出来,然后把它传输给user级的application,然后application再次把同样的内容再传回给处于kernal级的socket。这种场景下,application实际上只是作为一种低效的中间介质,用来把disk file的data传给socket。

    data每次穿过user-kernel boundary,都会被copy,这会消耗cpu,并且占用RAM的带宽。幸运的是,你可以用一种叫做Zero-Copy的技术来去掉这些无谓的 copy。应用程序用zero copy来请求kernel直接把disk的data传输给socket,而不是通过应用程序传输。Zero copy大大提高了应用程序的性能,并且减少了kernel和user模式的上下文切换

    

    使用kernel buffer做中介(而不是直接把data传到user buffer中)看起来比较低效(多了一次copy)。然而实际上kernel buffer是用来提高性能的。在进行读操作的时候,kernel buffer起到了预读cache的作用。当写请求的data size比kernel buffer的size小的时候,这能够显著的提升性能。在进行写操作时,kernel buffer的存在可以使得写请求完全异步。

      悲剧的是,当请求的data size远大于kernel buffer size的时候,这个方法本身变成了性能的瓶颈。因为data需要在disk,kernel buffer,user buffer之间拷贝很多次(每次写满整个buffer)。

    而Zero copy正是通过消除这些多余的data copy来提升性能。

传统的数据复制方式及涉及到的上下文切换:

      通过网络把一个文件传输给另一个程序,在OS的内部,这个copy操作要经历四次user mode和kernel mode之间的上下文切换,甚至连数据都被拷贝了四次,如下图:

    具体步骤如下:

  1. read() 调用导致一次从user mode到kernel mode的上下文切换。在内部调用了sys_read() 来从文件中读取data。第一次copy由DMA (direct memory access)完成,将文件内容从disk读出,存储在kernel的buffer中。
  2. 然后请求的数据被copy到user buffer中,此时read()成功返回。调用的返回触发了第二次context switch: 从kernel到user。至此,数据存储在user的buffer中。
  3. send() Socket call 带来了第三次context switch,这次是从user mode到kernel mode。同时,也发生了第三次copy:把data放到了kernel adress space中。当然,这次的kernel buffer和第一步的buffer是不同的buffer。
  4. 最终 send() system call 返回了,同时也造成了第四次context switch。同时第四次copy发生,DMA egine将data从kernel buffer拷贝到protocol engine中。第四次copy是独立而且异步的。

        Traditional data copying approach

        Traditional context switches


数据转移(data transfer): zero copy方式及涉及的上下文转换

        在linux 2.4及以上版本的内核中(如linux 6或centos 6以上的版本),开发者修改了socket buffer descriptor,使网卡支持 gather operation,通过kernel进一步减少数据的拷贝操作。这个方法不仅减少了context switch,还消除了和CPU有关的数据拷贝。user层面的使用方法没有变,但是内部原理却发生了变化:

  1. transferTo()方法使得文件内容被copy到了kernel buffer,这一动作由DMA engine完成。
  2. 没有data被copy到socket buffer。取而代之的是socket buffer被追加了一些descriptor的信息,包括data的位置和长度。然后DMA engine直接把data从kernel buffer传输到protocol engine,这样就消除了唯一的一次需要占用CPU的拷贝操作。

        Data copies when transferTo() and gather operations are used

            Context switching when using transferTo()


代码样例:

展示通过网络把一个文件从client传到server的过程

package zerocopy;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class TransferToServer {
	ServerSocketChannel listener = null;

	protected void mySetup() {
		InetSocketAddress listenAddr = new InetSocketAddress(9026);

		try {
			listener = ServerSocketChannel.open();
			ServerSocket ss = listener.socket();
			ss.setReuseAddress(true);
			ss.bind(listenAddr);
			System.out.println("监听的端口:" + listenAddr.toString());
		} catch (IOException e) {
			System.out.println("端口绑定失败 : "
					+ listenAddr.toString() + " 端口可能已经被使用,出错原因: "
					+ e.getMessage());
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {
		TransferToServer dns = new TransferToServer();
		dns.mySetup();
		dns.readData();
	}

	private void readData() {
		ByteBuffer dst = ByteBuffer.allocate(4096);
		try {
			while (true) {
				SocketChannel conn = listener.accept();
				System.out.println("创建的连接: " + conn);
				conn.configureBlocking(true);
				int nread = 0;
				while (nread != -1) {
					try {
						nread = conn.read(dst);
					} catch (IOException e) {
						e.printStackTrace();
						nread = -1;
					}
					dst.rewind();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
package zerocopy;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class TransferToClient {

	public static void main(String[] args) throws IOException {
		TransferToClient sfc = new TransferToClient();
		sfc.testSendfile();
	}

	public void testSendfile() throws IOException {
		String host = "localhost";
		int port = 9026;
		SocketAddress sad = new InetSocketAddress(host, port);
		SocketChannel sc = SocketChannel.open();
		sc.connect(sad);
		sc.configureBlocking(true);

		String fname = "src/main/java/zerocopy/test.data";
		FileChannel fc = new FileInputStream(fname).getChannel();
		long start = System.nanoTime();
		long nsent = 0, curnset = 0;
		curnset = fc.transferTo(0, fc.size(), sc);
		System.out.println("发送的总字节数:" + curnset
				+ " 耗时(ns):"
				+ (System.nanoTime() - start));
		try {
			sc.close();
			fc.close();
		} catch (IOException e) {
			System.out.println(e);
		}
	}
}

其它zero copy的用法 

package zerocopy;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

public class ZerocopyDemo {
	@SuppressWarnings("resource")
	public static void transferToDemo(String from, String to) throws IOException {
		FileChannel fromChannel = new RandomAccessFile(from, "rw").getChannel();
		FileChannel toChannel = new RandomAccessFile(to, "rw").getChannel();

		long position = 0;
		long count = fromChannel.size();

		fromChannel.transferTo(position, count, toChannel);
		
		fromChannel.close();
		toChannel.close();
	}

	@SuppressWarnings("resource")
	public static void transferFromDemo(String from, String to)
			throws IOException {
		FileChannel fromChannel = new FileInputStream(from).getChannel();
		FileChannel toChannel = new FileOutputStream(to).getChannel();

		long position = 0;
		long count = fromChannel.size();

		toChannel.transferFrom(fromChannel, position, count);
		
		fromChannel.close();
		toChannel.close();
	}
	
	public static void main(String[] args) throws IOException {
		String from="src/main/java/zerocopy/1.data";
		String to="src/main/java/zerocopy/2.data";
//		transferToDemo(from,to);
		transferFromDemo(from,to);
	}
}

参考

https://www.ibm.com/developerworks/linux/library/j-zerocopy/

http://blog.csdn.net/flyingqr/article/details/6942645

© 著作权归作者所有

共有 人打赏支持
cloud-coder
粉丝 243
博文 189
码字总数 135000
作品 0
广州
架构师
It's all about buffers: zero-copy, mmap and Java NIO

Sep 10, 2016 in OS There are use cases where data need to be read from source to a sink without modification. In code this might look quite simple: for example in Java, you may ......

不道归来
2017/10/23
0
0
Class SafeArray

Class SafeArray public final class SafeArray{ // Constructors public SafeArray(int vt); public SafeArray(int vt, int celems); public SafeArray(int vt, int celems1, int celems2);......

小青_1989
2014/06/06
0
1
JAVA NIO之浅谈内存映射文件原理与DirectMemory

JAVA类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段。本文我主要想结合操作系统中(OS)相关方面的知识...

pczhangtl
2013/11/19
0
0
Java编程基础知识点和技术点归纳

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰
05/23
0
0
JVM Management API

JVM本 身提供了一组管理的API,通过该API,我们可以获取得到JVM内部主要运行信息,包括内存各代的数据、JVM当前所有线程及其栈相关信息等等。各种 JDK自带的剖析工具,包括jps、jstack、jin...

今幕明
2014/09/09
0
0
书单丨5本Java后端技术书指引你快速进阶

一名Java开发工程师 不仅要对Java语言及特性有深层次的理解 而且需要掌握与Java相关的 框架、生态及后端开发知识 本文涉及多种后端开发需要掌握的技能 对于帮助提高开发能力非常有帮助 NO.1...

Java高级架构
05/30
0
0
Ubuntu 11.04 下安装配置 JDK 7

第一步:下载jdk-7-linux-i586.tar.gz [plain]view plaincopy wget -c http://download.oracle.com/otn-pub/java/jdk/7/jdk-7-linux-i586.tar.gz (注:如果下载不下来,建议使用迅雷下载,然......

木子叶
2012/06/19
0
1
从几个sample来学习Java堆,方法区,Java栈和本地方法栈

最近在看《深入理解Java虚拟机》,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构: 感觉有必要通过几个...

LCZ777
2014/08/31
0
0
Java程序员必读书单,家族又添新成员

点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书。 ——异步小编 有些革命出其不意地吸引了全世界的眼球。Twitter、Linux操作系统和...

异步社区
05/09
0
0
JNI 之二 :java & c/c++ 相互通信及调用

JNI是Java Native Interface的缩写,JNI是一种机制,有了它就可以在java程序中调用其他native代码,或者使native代码调用java层的代码。也就是说,有了JNI我们可以使Android项目中,java层与...

LiSteven
2013/04/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

设计模式

1.装饰器模式 概念 允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。 实现 增加一个修饰类包裹原来的...

EasyProgramming
11分钟前
1
0
用python2和opencv进行人脸识别

一、安装cv2 sudo apt-get install python-opencv opencv-data 二、 Haar特征分类器 Haar特征分类器就是一个XML文件,该文件中会描述人体各个部位的Haar特征值。包括人脸、眼睛、嘴唇等等。 ...

wangxuwei
11分钟前
0
0
python模板中循环字典

{% for k,v in user.items %} {{ k}} {{ v}} {% endfor %}

南桥北木
39分钟前
0
0
Java8系列之重新认识HashMap

简介 Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMap,类继承关系如下图所示: 下面针对各个实现类...

HOT_POT
43分钟前
0
0
获取调用方的className

/** * 获取调用方的class * @return */private static String getInvoke() { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); S......

iborder
今天
0
0
深入了解一下Redis的内存模型!

一前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时,会接触Redis的5种对象类型(字符...

Java填坑之路
今天
1
0
从实践出发:微服务布道师告诉你Spring Cloud与Spring Boot他如何选择

背景 随着公司业务量的飞速发展,平台面临的挑战已经远远大于业务,需求量不断增加,技术人员数量增加,面临的复杂度也大大增加。在这个背景下,平台的技术架构也完成了从传统的单体应用到微...

老道士
今天
1
0
大数据学习的各个阶段

第一阶段:Linux课程讲解Linux基础操作,讲的是在命令行下进行文件系统的操作,这是Hadoop学习的基础,后面的所有视频都是基于linux操作的。鉴于很多学员没有linux基础,特增加该内容,保证零linux...

董黎明
今天
0
0
CVE-2013-0077 堆溢出分析

找了很久才发现这个环境比较容易搭建分析... 环境: 系统---Win XP SP3 漏洞程序:QQPlayer 3.7.892.400 出错DLL:quartz.dll 6.5.2600.5512 调试工具:x32db+gflag.exe 过程: 首先gflag设置...

Explorer0
今天
7
0
CISCO VPN Client Reason 442 WIN8/10错误解决方案

http://jdkleo.iteye.com/blog/2163493 引用 http://my.oschina.net/cloudcoder/blog/220391?p={{currentPage 1}} 在使用cisco VPN 客户端登录时,产生Reason 442:Failedto enable Virtual......

chenfj_fer
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部