8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
Linux Zero-copy机制与Java NIO对Zero-copy应用
关于Zero-copy(零拷贝)维基百科
零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定区域。这种技术通常用于通过网络传输文件时节省CPU周期和内存带宽。原理
操作系统某些组件(例如驱动程序、文件系统和网络协议栈)若采用零复制技术,则能极大地增强了特定应用程序的性能,并更有效地利用系统资源。通过使CPU得以完成其他而非将机器中的数据复制到另一处的任务,性能也得到了增强。另外,零复制操作减少了在用户空间与内核空间之间切换模式的次数。
举例来说,如果要读取一个文件并通过网络发送它,传统方式下每个读/写周期都需要复制两次数据和切换两次上下文,而数据的复制都需要依靠CPU。通过零复制技术完成相同的操作,上下文切换减少到两次,并且不需要CPU复制数据。
零复制协议对于网络链路容量接近或超过CPU处理能力的高速网络尤为重要。在这种网络下,CPU几乎将所有时间都花在复制要传送的数据上,因此将成为使通信速率低于链路容量的瓶颈。
Linux I/O机制I/O中断
用户进程需要读取磁盘数据,需要CPU中断,发起IO请求,每次的IO中断,都带来CPU的上下文切换。
DMA(Direct Memory Access,直接内存存取)
DMA允许不同速度的硬件装置来沟通,而不需要依赖于CPU 的大量中断负载。DMA控制器接管了数据读写请求,减少CPU的负担。
涉及流程:
传统Socket传送数据大部分Web应用服务器都通过文件读取数据然后将数据通过网络传输给其他的程序的方式。
12File.read(fileDesc,tmp_buf,len);
File.send(socket,tmp_buf,len);数据从文件到socket的过程
用户态和内核态的切换过程
涉及过程
12345671.read()的调用引起了从用户态到内核态的切换,内部是通过sys_read()(或者类似的方法)发起对文件数据的读取。数据的第一次复制是通过DMA(直接内存访问)将磁盘上的数据复制到内核空间的缓冲区中。
2.数据从内核空间的缓冲区复制到用户空间的缓冲区后,read()方法也就返回了。此时内核态又切换回用户态,现在数据也已经复制到了用户地址空间的缓冲区中。
3.socket的send()方法的调用又引起了用户态到内核态的切换,第三次数据复制有将数据从用户空间缓冲区复制到了内核空间的缓冲区,这次数据被放在了不同于之前的内核缓冲区中,这个缓冲区与数据将要被传输到socket关联。
4.send()系统调用返回后,就产生了第四次用户态和内核态的切换。随着DMA单独异步的将数据从内核态的缓冲区中传输到协议引擎发送到网络上,有了第四次数据的复制。
问题传统数据传送所消耗的成本:4次拷贝(两次是DMA copy,两次是CPU copy),4次上下文切换。
大量的数据复制并不是真正需要的。可以消除一些重复,以减少开销并提高性能。
内核缓冲区做中介的引入虽然改善了进程的性能(中介缓冲区可以用来实现异步功能,当缓冲区数据满了之后再写上去减少系统调用次数),当应用程序读取的数据比这个中介缓冲区的容量大很多的时候,数据就会在磁盘、内核空间、用户空间之间复制多次后才最终被传给应用程序。
改善零拷贝技术(Zero-copy),在OS层面优化,减少IO流程中不必要的拷贝。
Linux Zero-copy
mmap(内存映射)
12tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);
核心:从磁盘加载的数据通过DMA拷贝存储在内核缓冲区中。
然后将应用程序缓冲区的页面映射到内核缓冲区,以便省略内核缓冲区和应用程序缓冲区之间的数据复制。
read-send模型
优化:
拷贝次数优化(1次cpu copy,2次DMA copy)。
上下文切换四次。
sendfile
在内核版本2.1中,引入了sendfile系统调用,以简化网络和两个本地文件之间的数据传输。sendfile的引入不仅减少了数据复制,还减少了上下文切换。
1sendfile(socket, file, len);
在调用sendfile()系统调用时,数据从磁盘中获取并通过DMA复制复制到内核缓冲区中。
然后将数据直接从内核缓冲区复制到套接字缓冲区。
将所有数据复制到套接字缓冲区后,sendfile()系统调用将返回以指示从内核缓冲区到套接字缓冲区的数据传输完成。
然后,数据将被复制到网卡上的缓冲区并传输到网络。
read-send模型
优化:拷贝次数优化(1次CPU copy,2次DMA copy)
上下文切换两次
Sendfile With DMA Scatter/Gather Copy
在Linux内核2.4以后的版本中, linux内核对socket缓冲区描述符做了优化. 通过这次优化, sendFile系统调用可以在只复制kernel buffer的少量元信息的基础上, 把数据直接从kernel buffer 复制到网卡的buffer中去.从而避免了从”内核缓冲区”拷贝到”socket缓冲区”的这一次拷贝.
read-send模型
优化:拷贝次数优化 (0次cpu copy,2次DMA copy)
上下文切换两次
splice
Linux内核2.6.17 支持splice。splice不需要在内核空间和用户空间之间复制数据。使用此方法时,数据从磁盘读取到OS内核缓冲区后,在内核缓冲区直接可将其转成内核空间其他数据buffer,而不需要拷贝到用户空间。与Sendfile With DMA Scatter/Gather Copy不同,splice()不需要硬件支持。
与sendFile的区别:sendfile是将磁盘数据加载到kernel buffer后,需要一次CPU copy,拷贝到socket buffer。splice不需要CPU copy
优化:拷贝次数优化 (0次cpu copy,2次DMA copy)
上下文切换两次
Zero-copy机制对比
Java NIO 对Linux Zero-copy机制支持
NIO中的mmap
123MappedByteBuffer mappedByteBuffer = new RandomAccessFile(file, "r")
.getChannel()
.map(FileChannel.MapMode.READ_ONLY, 0, 1024);FileChannle.map(MapMode mode,long position, long size)
将Channel文件的某个区域直接映射到内存中。采用了操作系统中的内存映射方式,底层就是调用Linux mmap()实现的。
将内核缓冲区的内存和用户缓冲区的内存做了一个地址映射。这种方式适合读取大文件,同时也能对文件内容进行更改,但是如果其后要通过SocketChannel发送,还是需要CPU进行数据的拷贝。注意点
使用mmap文件映射,只有在JVM在full gc时才会进行释放内存。当close时,需要手动清除内存映射文件,可以反射调用sun.misc.Cleaner方法。
NIO中的sendfileFileChannel.transferTo(long position, long count,WritableByteChannel target)
transferTo方法直接将当前通道内容传输到另一个通道,没有涉及到Buffer的任何操作,NIO中 的Buffer是JVM堆或者堆外内存(操作系统内核空间的内存)。Linux系统中通过系统调用sendfile() 实现。Zero-copy实现文件复制
123456789public void copyFile(File src, File dest) {
try (FileChannel srcChannel = new FileInputStream(src).getChannel();
FileChannel destChannel = new FileInputStream(dest).getChannel()) {
srcChannel.transferTo(0, srcChannel.size(), destChannel);
} catch (IOException e) {
e.printStackTrace();
}
}