JAVA Zero Copy的相关知识【转】
转自:https://my.oschina.net/cloudcoder/blog/299944
摘要: java 的zero copy多在网络应用程序中使用。Java的libaries在linux和unix中支持zero copy,关键的api是java.nio.channel.FileChannel的transferTo(),transferFrom()方法。我们可以用这两个方法来把bytes直接从调用它的channel传输到另一个writable byte channel,中间不会使data经过应用程序,以便提高数据转移的效率。
介绍
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之间的上下文切换,甚至连数据都被拷贝了四次,如下图:
具体步骤如下:
- read() 调用导致一次从user mode到kernel mode的上下文切换。在内部调用了sys_read() 来从文件中读取data。第一次copy由DMA (direct memory access)完成,将文件内容从disk读出,存储在kernel的buffer中。
- 然后请求的数据被copy到user buffer中,此时read()成功返回。调用的返回触发了第二次context switch: 从kernel到user。至此,数据存储在user的buffer中。
- send() Socket call 带来了第三次context switch,这次是从user mode到kernel mode。同时,也发生了第三次copy:把data放到了kernel adress space中。当然,这次的kernel buffer和第一步的buffer是不同的buffer。
- 最终 send() system call 返回了,同时也造成了第四次context switch。同时第四次copy发生,DMA egine将data从kernel buffer拷贝到protocol engine中。第四次copy是独立而且异步的。


数据转移(data transfer): zero copy方式及涉及的上下文转换
在linux 2.4及以上版本的内核中(如linux 6或centos 6以上的版本),开发者修改了socket buffer descriptor,使网卡支持 gather operation,通过kernel进一步减少数据的拷贝操作。这个方法不仅减少了context switch,还消除了和CPU有关的数据拷贝。user层面的使用方法没有变,但是内部原理却发生了变化:
- transferTo()方法使得文件内容被copy到了kernel buffer,这一动作由DMA engine完成。
- 没有data被copy到socket buffer。取而代之的是socket buffer被追加了一些descriptor的信息,包括data的位置和长度。然后DMA engine直接把data从kernel buffer传输到protocol engine,这样就消除了唯一的一次需要占用CPU的拷贝操作。


代码样例:
展示通过网络把一个文件从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/
JAVA Zero Copy的相关知识【转】的更多相关文章
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- [转]java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- Java基础--面向对象以及相关知识
一. 面向对象特征与原则 1.三大特征:封装.继承.多态. (1)封装:将客观的事物封装成抽象的类,封装代码逻辑,并通过访问控制符来控制访问的方式,从而保护程序(使用反射时,可以获取对象的私有方法和成 ...
- java中数组的相关知识
1. 2.数组的命名方法 1)int[]ages=new int[5]; 2) int[]ages; ages=new int[5]; 3)int[]ags={1,2,3,4,5}; 4)int[ ...
- Java常用类Date相关知识
Date:类 Date 表示特定的瞬间,精确到毫秒. 在 JDK 1.1 之前,类 Date 有两个其他的函数.它允许把日期解释为年.月.日.小时.分钟和秒值.它也允许格式化和解析日期字符串. Dat ...
- 面试4——java进程和线程相关知识
1.线程和进程的概念.并行和并发的概念
- Java 容器相关知识全面总结
Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题.因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAda ...
- 【转】java NIO 相关知识
原文地址:http://www.iteye.com/magazines/132-Java-NIO Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的 ...
- Java并发编程学习前期知识下篇
Java并发编程学习前期知识下篇 通过上一篇<Java并发编程学习前期知识上篇>我们知道了在Java并发中的可见性是什么?volatile的定义以及JMM的定义.我们先来看看几个大厂真实的 ...
随机推荐
- android 用代码画虚线边框背景
1.虚线画效果,可以使用Android中的xml来做. 2.直接上代码: <RelativeLayout android:id="@+id/coupon_ ...
- <转>Boost库之asio io_service以及run、run_one、poll、poll_one区别
本文转自:http://blog.csdn.net/byxdaz/article/details/71088812 一.io_service的作用 io_servie 实现了一个任务队列,这里的任务就 ...
- python实现json转excel
import json import xlwt a = json.load(open("haha.json", encoding='utf8')) title = list(set ...
- leetcode770. Basic Calculator IV
此题真可谓是练习编程语言的绝好材料 ! import java.util.*; class Solution { class Item { Map<String, Integer> var ...
- #探究# HTTP长连接和短连接
本文速读: HTTP协议与TCP/IP协议的关系 因TCP协议可靠,所以HTTP通常基于TCP实现 如何理解HTTP协议是无状态的 多次请求之间没有关联关系 什么是长连接.短连接? 每次请求都建立TC ...
- 自动化运维之Cobbler自动化部署安装操作系统
Cobbler概述: Cobbler可以用来快速建立 Linux 网络安装环境,它已将 Linux 网络安装的技术门槛,从大专以上文化水平,成功降低到初中以下,连补鞋匠都能学会. 在生产环境中,经常批 ...
- 2011最赚钱的行业和公司排行榜(verified 版本)
最赚钱的行业和公司排行榜(verified 版本) [外资证券]:代表性公司:高盛.中金.摩根士丹利等单位第一年收入:50-80万左右(中金第一年基本工资25万,奖金35万,福利10万)五年后收入:3 ...
- java 记录一下socket的server与client
package com.test.server; import java.io.IOException; import java.net.ServerSocket; import org.apache ...
- 关于使用coreseek并为其做分页的介绍(转)
coreseek 做分页时找数据总量还真不好找.以为他会给一个方法(函数)什么的去获取,结果却不是.首先需要了解:num_matches: 当前返回的结果数,<= limit设置值.max_ma ...
- TensorFlow 基本概念
一.概述 使用图(graph)来表示计算任务 在会话(Session)的上下文(context)中执行图(graph) 使用tensor表示数据 通过 变量(Variable)维护状态 使用 feed ...