应用场景:将本地一个文件经过网络传输给另外一个程序 关键字:数据复制过程当中,内容不进行修改java
zero-copy技术的使用场景有不少,好比Kafka, 又或者是Netty等,能够大大提高程序的性能 下面咱们经过对比传统方式和zero-copy的方式传输数据,来理解zero-copy。linux
代码以下:bash
// 将文件读取到buf中
File.read(fileDesc, buf, len);
// 将buf中的数据写入到socket中
Socket.send(socket, buf, len);
复制代码
结合下图理解理解操做以下:网络
结论: 以上操做要经历4次user mode和kernel mode之间的上下文切换,数据都被拷贝了4次。经过上面的分析,咱们发现第2步和第3步数据copy是多余,系统彻底能够将文件读取到kernel buffer中后,直接将kernel buffer中的数据写入socket。为了实现这个优化,linux引入了zero copy。异步
要支持zero-copy要知足以下条件: a. 操做系统的要求:linux 2.4及以上版本的内核中,而且网卡支持 gather operation。socket
java中使用 FileChannel的transferTo()和transferFrom()方法使用zero-copy,若是底层系统支持zero-copy性能
代码:测试
public void transferTo(long position, long count, WritableByteChannel target);
// transferTo()方法底层会调用系统方法sendfile()
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
复制代码
结合下图理解理解操做以下: a. 执行transferTo()方法后,会发生上下文切换,从user mode到kernel mode,而后将文件内容copy到kernel buffer(称为Read buffer),这一操做由DMA engine完成。。 b. 和传统方式不一样,zero-copy没有将数据copy到socket buffer,只有数据的描述信息(如数据的位置和长度)存储到socket buffer中。而后DMA engine直接把数据从kernel buffer传输到网卡设备(protocol engine)。 完成复制后,又会发生上下文切换,从kernel mode到user mode优化
结论: 1. Zero-copy上下文切换的次数从4次下降到2次,数据复制次数从4次下降到2次 2. Zero-copy 中数据的copy都由DMA执行,CPU不参与复制,从而节省CPU的消耗 3. Zero-copy中的zero不是指不须要copy,而是指user mode到kernel mode copy数据的次数为零ui
支持zero-copy的操做系统 除了Linux系统,其余系统也支持zero-copy • Microsoft Windows经过TransmitFile API支持zero-copy • macOS supports zero-copy through the FreeBSD portion of the kernel
下面咱们使用代码来比较传统方式和zero-copy的传输效率 测试代码:客服端从本地读取文件,经过网络传输到服务端
测试代码来源:developer.ibm.com/articles/j-… 不过在测试过程当中,此源码在测试有问题,咱们对测试代码进行修改: TransferToClient.java
// 原来的版本,这里会有问题,由于transferTo()一次最多发送8388608个字节,须要循环发送。
long position = 0;
while(curnset != 0) {
curnset = fc.transferTo(position, fsize, sc);
position += curnset;
System.out.println("total bytes transferred--" + position + " and time taken in MS--" + (System.currentTimeMillis() - start));
}
复制代码
TraditionalServer.java
// nread判断条件修改成 nread <=0,而不是nread==0
byte[] byteArray = new byte[4096];
while(true) {
int nread = input.read(byteArray , 0, 4096);
if (nread <= 0)
break;
}
}
复制代码
文件传输花费时间对比