美文网首页
零拷贝简析及Java中的相关实现

零拷贝简析及Java中的相关实现

作者: 视频怪物 | 来源:发表于2019-12-22 16:04 被阅读0次

    零拷贝, DMA(Direct Memory Access) copy, 即直接内存拷贝(不使用CPU拷贝), 是网络编程的关键,很多IO相关的性能优化都离不开。

    传统数据copy方式

    经过了4次拷贝和3次状态切换, 其中有2次DMA Copy

    传统数据copy

    步骤

    1. 从硬盘中读取数据, 使用DMA Copy拷贝到内核buffer中
    2. 再使用CPU Copy将数据从内核buffer中拷贝到用户buffer
    3. 再使用CPU Copy将数据从用户buffer拷贝到socket buffer
    4. 再使用DMA Copy将数据从socket buffer拷贝到协议栈中

    mmap优化

    经过了3次拷贝和3次状态切换, 其中有2次DMA copy

    mmap优化

    原理

    memory map(内存映射), 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户控件的拷贝次数。

    步骤

    1. 从硬盘中读取数据, 使用DMA Copy拷贝到内核buffer中
    2. 使用mmap将文件映射到内核buffer中和用户buffer共享, 无需再次拷贝到用户buffer中
    3. 再使用CPU Copy将数据从用户buffer拷贝到socket buffer
    4. 再使用DMA Copy将数据从socket buffer拷贝到协议栈中

    sendFile优化

    Linux 2.4 -

    经过了3次拷贝和2次状态切换, 其中有2次DMA copy

    Linux 2.4- 版本 sendFile
    原理

    Linux在2.4版本之前提供的 sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 Socket Buffer,同时,由于和用户态完全无关,就减少了一次上下文切换。

    步骤
    1. 从硬盘中读取数据, 使用DMA Copy拷贝到内核buffer中
    2. 再使用CPU Copy将数据从内核buffer直接拷贝到socket buffer
    3. 再使用DMA Copy将数据从socket buffer拷贝到协议栈中

    Linux 2.4 +

    经过了2次拷贝, 其中近似没有CPU copy (可以看作完全实现了零拷贝)

    Linux 2.4+ 版本

    原理

    相对于之前Linux版本中的sendFile做了一些修改,极大地避免了从内核缓冲区CPU Copy到 Socket buffer 的操作(这里其实会有一次cpu拷贝,但是拷贝的信息很少, 主要是一些描述信息, 比如lenght , offset, 这些信息会从kernel buffer拷贝到socket buffer, 但是过程中的消耗非常低,可以忽略),直接拷贝到协议栈,从而再一次减少了数据拷贝。

    Java Nio 零拷贝

    使用transferTo()/transferFrom()函数

    原理

    /*
     * <p> This method is potentially much more efficient than a simple loop
     * that reads from the source channel and writes to this channel.  Many
     * operating systems can transfer bytes directly from the source channel
     * into the filesystem cache without actually copying them.  </p>
     * 
     */ 
    public abstract long transferFrom(ReadableByteChannel src, long position, long count) throws IOException;
    

    许多操作系统可以直接将字节从源通道转移到文件系统缓存中,而无需实际复制它们, 而transferTo()/transferFrom()函数底层使用MappedByteBuffer来实现, 可以让文件直接在内存(堆外内存)中进行修改, 操作系统不需要再拷贝一次, MappedByteBuffer操作内存相关代码请点击查看

    代码

    public static void transferFrom() {
        File sourceFile = new File("./source.log");
        File targetFile = new File("./target1.log");
        try (
                FileInputStream fileInputStream = new FileInputStream(sourceFile);
                FileChannel fileInputStreamChannel = fileInputStream.getChannel();
                FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
                FileChannel fileOutputStreamChannel = fileOutputStream.getChannel();
        ) {
            fileOutputStreamChannel.transferFrom(fileInputStreamChannel, 0, fileInputStreamChannel.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    相关文章

      网友评论

          本文标题:零拷贝简析及Java中的相关实现

          本文链接:https://www.haomeiwen.com/subject/dxponctx.html