网站建设集群化的必要,深圳建设银行网站,wordpress图片本地化,网络服务费要交印花税吗1.DMA技术详解
#xff08;1#xff09;应用程序 从 磁盘读写数据 的时序图#xff08;未用DMA技术前#xff09; #xff08;2#xff09;什么是DMA 技术 (Direct Memory Access#xff09;
直接内存访问#xff0c;直接内存访问是计算机科学中的一种内存访问技术。…1.DMA技术详解
1应用程序 从 磁盘读写数据 的时序图未用DMA技术前 2什么是DMA 技术 (Direct Memory Access
直接内存访问直接内存访问是计算机科学中的一种内存访问技术。DMA之前要把外设的数据读入内存或把内存的数据传送到外设一般都要通过CPU控制完成利用中断技术。允许某些硬件系统能够独立于CPU直接读写操作系统的内存不需要中处理器CPU介入处理。数据传输操作在一个DM控制器DMAC的控制下进行在传输过程中CPU可以继续进行其他的工作。在大部分时间CPU和I/O操作都处于并行状态系统的效率更高。 3应用程序的读写数据 读本地磁盘 操作系统检查内存缓冲区读取如果存在则直接把内核空间的数据copy到用户空间CPU负责应用程序即可使用。 上步没数据则从磁盘中读取到内核缓冲DMA负责再把内核空间的数据copy到用户空间CPU负责应用程序即可使用 硬盘-内核缓冲区-用户缓冲区 写操作本地磁盘 根据操作系统的写入方式不一样buffer IO 和 direct IO 写入磁盘时机不一样。buffer IO 应用程序把数据从用户空间copy到内核空间的缓冲区CPU负责再把内核缓冲区的数据写到磁盘DMA负责。 direct IO 应用程序把数据直接从用户态地址空间写入到磁盘中直接跳过内核空间缓冲区。减少操作系统缓冲区和用户地址空间的拷贝次数降低了CPU和内存开销。 用户缓冲区-内核缓冲区-硬盘 读网络数据 网卡Socket类似磁盘中读取客户端发送的数据到内核空间DMA负责。把内核空间的数据copy到用户空间CPU负责然后应用程序即可使用。 写网络数据 用户缓冲区中的数据copy到内核缓冲区的Socket Buffer 中CPU负责将内核空间中的Socket Buffer 拷贝到Socket协议栈网卡设备进行传输DMA负责
4DMA的工作总结
从磁盘的缓冲区到内核缓冲区的拷贝工作。从网卡设备到内核的socket buffer 的拷贝工作。从内核缓冲区到磁盘缓冲区的拷贝工作。从内核的socket buffer到网卡设备的拷贝工作。注意内核缓冲区到用户缓冲区之间的拷贝工作仍然由CPU负责
5DMA技术带来的性能损耗 上图应用程序从磁盘读取数据发送到网络上的损耗程序需要两个命令 先read读取再write写出四次内核态和用户态的切换四次缓冲区的拷贝2次DMA拷贝、2次CPU拷贝 读取磁盘缓冲区到内核缓冲区DMA读取内核缓冲区到用户缓冲区CPU写出用户缓冲区到内核缓冲区Socket BufferCPU写出内核缓冲区的Socket Buffer到网卡设备DMA
为了解决这种性能的损耗所以就诞生了零拷贝。
2.ZeroCopy零拷贝技术简介
1什么是零拷贝ZeroCopy
减少不必要的内核缓冲区跟用户缓冲区之间的拷贝工作从而减少CPU的开销和减少kernel和user模式的上下文切换达到性能的提升。从磁盘中读取文件通过网络发送出去只需要拷贝2\3次和2\4的内核态和用户态的切换即可。
ZeroCopy技术实现方式有两种内核态和用户态切换次数不一样
方式一mmapwrite方式二sendfile
2ZeroCopy的实现底层 mmap write 操作系统都使用虚拟内存虚拟地址通过多级页表映射物理地址。 多个虚拟内存可以指向同一个物理地址虚拟内存的总空间远大于物理内存空间。 如果把内核空间和用户空间的虚拟地址映射到同一个物理地址就不需要来回复制数据。 mmap系统调用函数会直接把内核缓冲区的数据映射到用户空间内核空间和用户空间就不需要在进行数据拷贝的操作了节省了CPU开销。 mmap()负责读取write()负责写出 执行流程 应用程序先调用mmap()方法将数据从磁盘拷贝到内核缓冲区返回结束DMA负责。在调用write()内核缓冲区的数据直接拷贝到内核socket buffer CPU负责然后把内核缓冲区的Socket Buffer 给直接拷贝给Socket协议线即网卡设备中返回结束DMA负责 采用mmap之后CPU用户态和内核态上下文切换依旧是4次和全程有3次数据拷贝2次DMA拷贝、1次CPU拷贝、4次内核态用户态切换减少了1次CPU拷贝
3ZeroCopy的实现底层 sendfile
Linux kernal 2.1新增发送文件的系统调用函数sendfile()。执行流程 替代read()和write()两个系统调用减少一次系统调用即减少2次CPU上下文切换的开销调用sendfile()从磁盘读取到内核缓冲区然后直接把内核缓冲区的数据拷贝到socket buffer缓冲区里再把内核缓冲区的SocketBuffer给直接拷贝给Socket协议栈即网卡设备中DMA负责。 采用sendfile后CPU用户态和内核态上下文切换是2次 和 全程3次的数据拷贝2次DMA拷贝、1次的CPU拷贝、2次内核态用户态切换。Linux 2.4 版本之后改进sendfile利用DMA Gather(带有收集功能的DMA)变成了真正的零拷贝没有CPU Copy 应用程序先调用sendfile()方法将数据从磁盘拷贝到内核缓冲区DMA负责 把内存地址、偏移量的缓冲区fd描述符拷贝到Socket Buffer中去 拷贝很少的数据可忽略 本质和虚拟内存的解决方法思路一样就是内存地址的记录 然后把内核缓冲区的Socket Buffer给直接拷贝给Socket协议栈 即网卡设备中返回结束DMA负责 3.Java和主流中间件里的零拷贝技术
1Java中有哪些零拷贝技术
Java NIO对mmap的实现 fileChannel.map()Java NIO对sendfile的实现 fileChannel.transferTo() 和 fileChannel.transferFrom()
2什么是FileChannel
FileChannel是一个连接到文件的通道可以通过文件通道读写文件该常被用于搞笑的网络/文件的数据传输和大文件拷贝应用程序使用FileChannel写完以后数据是在PageCache上的操作系统不定时的把PageCache的数据写入到磁盘。为了避免宕机数据丢失使用channel.force(true) 把文件相关的数据强制刷入磁盘上去。使用之前必须先打开它但是无法直接new一个FileChannel。常规通过使用一个InputStream、OutputStream或者RandomAccessFile来获取一个FileChannel实例。
RandomAccessFile randomAccessFile new RandomAccessFile(文件路径,rw);
FileChannel inChannel randomAccessFile.getChannel();3mmap方式实现
map方法把文件映射成内存映射文件MappedByteBuffer是抽象类也是ByteBuffer的子类具体实现子类是DirectByteBuffer可被通道进行读写。一次map大小要限制在2G内过大map会增加虚拟内存回收和重新分配的压力直接报错。FileChannel.java中的map对long size 进行了限制不能大于Integer.MAX_VALUE否则就报错 JDK层做限制是因为底层C的类型无符号int类型最大是2^31 -1 2^31 -1 字节就是 2GB - 1B。
MappedByteBuffer map(int mode,long position,long size)
position:文件开始位置
size:映射文件区域大小
mode:访问该内存映射文件的方式READ_ONLY(只读)、READ_WRITE(读写)、PRIVATE(创建一个读写副本)4sendfile方式实现
fileChannel.transferTo(long postition,long count,WritableByteChannel target)将字节从此通道的文件传输到给定的可写入字节通道。返回值为真实拷贝的size最大拷贝2G超出2G的部分将丢弃。
position:文件中的位置从此位置开始传输必须非负数
count:要传输的最大字节数必须非负数
target:目标通道
返回:实际已传输的字节数可能为零fileChannel.transferFrom(ReadableByteChannel src, long position, long count)
将字节从给定的可读取字节通道传输到此通道的文件中对比 从源通道读取并将内容写入此通道的循环语句相比此方法更高效
src:源通道
position:文件中的位置从此位置开始传输,必须非负数
count:要传输的最大字节数, 必须非负数
返回:实际已传输的字节数可能为零transferFrom允许将一个通道连接到另一个通道不需要在用户态和内核态来回复制同时通道的内核态数据也无需复制transferTo只有源为FileChannel才支持transfer这种搞笑的复制方式其他如SocketChannel都不支持transfer模式。一般可以做FileChannel-FileChannel-FileChannel 和 FileChannel-SocketChannel的transfer零拷贝
4.文件IO性能对比实战
实现一个文件拷贝对比不同IO方式性能差异,文件大小 200MB5GB
编码实现
普通java的io流普通java的带buffer的io零拷贝实现之mmap的io零拷贝实现之sendfile的io
运行环境准备
Linux CentOS7.X安装JDK11 配置全局环境变量 vi /etc/profile
JAVA_HOME/usr/local/jdk11
CLASSPATH$JAVA_HOME/lib/
PATH$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH环境变量立刻生效 source /etc/profile 查看安装情况 java -version 准备1.34G测试文件 1普通java的io验证
public class IOTest {public static void main(String[] args) {String inputFilePath args[0];String outputFilePath args[1];long start System.currentTimeMillis();try (FileInputStream fis new FileInputStream(inputFilePath);FileOutputStream fos new FileOutputStream(outputFilePath)) {byte[] buf new byte[1];while(fis.read(buf) ! -1){fos.write(buf);}} catch (IOException e) {e.printStackTrace();}long end System.currentTimeMillis();System.out.println(普通IO耗时(end-start));}测试java IOTest.java /usr/local/music.zip /usr/local/io-music.zip2普通java的带buffer的io
public class BufferIOTest {public static void main(String[] args) {String inputFilePath args[0];String outputFilePath args[1];long start System.currentTimeMillis();try (BufferedInputStream bis new BufferedInputStream(new FileInputStream(inputFilePath));BufferedOutputStream bos new BufferedOutputStream(new FileOutputStream(outputFilePath))) {byte[] buf new byte[1];while(bis.read(buf) ! -1){bos.write(buf);}} catch (IOException e) {e.printStackTrace();}long end System.currentTimeMillis();System.out.println(Buffer IO耗时(end-start));}
}3零拷贝实现之mmap的io
一次 map 最大支持2GB超过2GB会报错
public class MmapIOTest {public static void main(String[] args) {String inputFilePathStr args[0];String outputFilePathStr args[1];long start System.currentTimeMillis();try (FileChannel channelIn new FileInputStream(inputFilePathStr).getChannel();FileChannel channelOut new RandomAccessFile(outputFilePathStr, rw).getChannel()) {long size channelIn.size();System.out.println(mappedFile:size);MappedByteBuffer mbbi channelIn.map(FileChannel.MapMode.READ_ONLY, 0, size);MappedByteBuffer mbbo channelOut.map(FileChannel.MapMode.READ_WRITE, 0, size);for (int i 0; i size; i) {byte b mbbi.get(i);mbbo.put(i,b);}} catch (Exception e) {e.printStackTrace();}long end System.currentTimeMillis();System.out.println(mmap 零拷贝 IO 耗时:(end-start));}
}4零拷贝实现之sendfile的io
最大拷贝2G超出2G的部分将丢弃最终拷贝的文件大小只有2GB多点超过2GB可以考虑多次执行
public class SendFileIOTest {public static void main(String[] args) {String inputFilePathStr args[0];String outputFilePathStr args[1];long start System.currentTimeMillis();try (FileChannel channelIn new FileInputStream(inputFilePathStr).getChannel();FileChannel channelOut new FileOutputStream(outputFilePathStr).getChannel()) {// 代码一针对小于2GB的问题返回值为真实拷贝的size最大拷贝2G超出2G的部分将丢弃最终拷贝文件大小只有2GB//channelIn.transferTo(0,channelIn.size(),channelOut);// 代码二针对大于2GB的文件long size channelIn.size();for (long left size;left0;){//transferSize所拷贝过去的真实长度size - left 计算出下次要拷贝的位置long transferSize channelIn.transferTo((size - left),left,channelOut);System.out.println(总大小size拷贝大小transferSize);//left剩余字节多少left left - transferSize;}} catch (Exception e) {e.printStackTrace();}long end System.currentTimeMillis();System.out.println(sendfile 零拷贝 IO 耗时:(end-start));}
}5测试结果分析
1~2GB的文件
普通拷贝 普通java的io流【慢】3973924秒普通java的带buffer的io【快】33196秒 零拷贝 零拷贝实现之mmap的io【快】7131秒零拷贝实现之sendfile的io【快】1784秒
分析原因之前我们先来了解一下局部性原理
局部性原理指计算机在执行某个程序时倾向于使用最近使用的数据
时间局部性如果程序中的某条指令一旦被执行则不久的将来该指令可能再次被执行
空间局部性一旦程序访问了某个存储单元在不久的将来附近的存储单元也有可能被访问普通的IO和Buffer IO为什么带有Buffer的IO要比普通的IO性能高
每次读取数据的时候系统根据局部性原理通过DMA会读入更多的数据到内核缓冲区里面
OS根据局部性原理会在一次read()系统调用过程中预读更多的文件数据缓存在内核IO缓冲区中
当继续访问的文件数据在缓冲区中时便直接拷贝数据到进程缓冲区避免了再次的抵消磁盘IO操作
OS已经帮减少磁盘IO操作次数提高了性能两种零拷贝的方式对比
(1)sendfile无法在调用过程中修改数据只适用于应用程序不需要对所访问数据进行处理修改情况适合静态文件传输MQ的Broker发送消息给消费者。适合大文件传输2次上下文切换最少2次数据拷贝。(2)mmap在mmap调用可以在应用程序中直接修改Page Cache中的数据使用的是mmapwrite两步。调用比sendfile成本高但由于传统的拷贝方式适用于多个线程以只读的方式同时访问同一个文件mmap机制下多线程共享同一个物理内存空间节约内存。适合小数据量续写4次上下文切换3次数据拷贝。5.主流中间件中用到的ZeroCopy技术
1Nginx使用的是sendfile 零拷贝
WebServer处理静态页面请求时是从磁盘中读取网页的内容因为sendfile不能在应用程序中修改数据所以最适合静态文件服务器或者是直接转发数据的代理服务器。
2rocketmq主要是mmap也有小部分使用sendfile
rocketMQ在消息存盘和网络发送使用mmap, 单个CommitLog文件大小默认1GB 要在用户进程内处理数据然后再发送出去的话用户空间和内核空间的数据传输就是不可避免的
3Kafka主要是sendfile也有小部分使用mmap
kafka 在客户端和 broker 进行数据传输时broker 使用 sendfile 系统调用类似 【FileChannel.transferTo】 API将磁盘文件读到 OS 内核缓冲区后直接转到 socket buffer 进行网络发送即 Linux 的 sendfile。 中读取网页的内容因为sendfile不能在应用程序中修改数据所以最适合静态文件服务器或者是直接转发数据的代理服务器。
2rocketmq主要是mmap也有小部分使用sendfile
rocketMQ在消息存盘和网络发送使用mmap, 单个CommitLog文件大小默认1GB 要在用户进程内处理数据然后再发送出去的话用户空间和内核空间的数据传输就是不可避免的
3Kafka主要是sendfile也有小部分使用mmap
kafka 在客户端和 broker 进行数据传输时broker 使用 sendfile 系统调用类似 【FileChannel.transferTo】 API将磁盘文件读到 OS 内核缓冲区后直接转到 socket buffer 进行网络发送即 Linux 的 sendfile。