零拷贝Zero-copy

nio零拷贝https://www.jianshu.com/p/193cae9cbf07

基本概念

1.内核空间和用户空间(大小比例大约1:3)

操作系统和驱动程序运行在内核空间
应用程序运行在用户空间

操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备 的所有权限。为了保证用户进程不能直接操作内核 (kernel),保证内核的安全,操作系统将虚拟空间划 分为两部分,一部分为内核空间,一部分为用户空间。

https://blog.csdn.net/weixin_39731083/article/details/82345157
在这里插入图片描述

零拷贝 (零拷贝从操作系统角度,是没有cpu拷贝)

1.概念

零拷⻉主要的任务是避免CPU将数据从一块存储拷⻉到另外一块存储,主要就是利用各种拷⻉的技术, 避免让CPU做大量的数据拷⻉任务,减少不必要的拷⻉,或者说让别的组建来做这一类简单的数据传 输,让CPU专注于别的任务。这样就可以让系统资源利用的更加有效

2.传统IO和零拷贝分析

传统IO (四次上下文切换,四次拷贝)

在这里插入图片描述
在这里插入图片描述

  1. read 调用导致用户态到内核态的一次变化,同时,第一次复制开始:DMA(Direct Memory Access,直接内存存取,即不使用 CPU 拷贝数据到内存,而是 DMA 引擎传输数据到内存,用于解放 CPU) 引擎从磁盘读取 index.html 文件,并将数据放入到内核缓冲区。
  2. 发生第二次数据拷贝,即:将内核缓冲区的数据拷贝到用户缓冲区,同时,发生了一次用内核态到用户态的上下文切换。
  3. 发生第三次数据拷贝,我们调用 write 方法,系统将用户缓冲区的数据拷贝到 Socket 缓冲区。此时,又发生了一次用户态到内核态的上下文切换。
  4. 第四次拷贝,数据异步的从 Socket 缓冲区,使用 DMA 引擎拷贝到网络协议引擎。这一段,不需要进行上下文切换。
  5. write 方法返回,再次从内核态切换到用户态。

mmap优化 (四次上下文切换,三次数据拷贝)

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

在这里插入图片描述
在这里插入图片描述
如上图,user buffer 和 kernel buffer 共享 index.html。如果你想把硬盘的 index.html 传输到网络中,再也不用拷贝到用户空间,再从用户空间拷贝到 Socket 缓冲区。
现在,你只需要从内核缓冲区拷贝到 Socket 缓冲区即可,这将减少一次内存拷贝(从 4 次变成了 3 次),但不减少上下文切换次数。

sendFile 优化 【直接传递文件描述】(三次上下文切换,最少两次数据拷贝)

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

在这里插入图片描述

在这里插入图片描述
如上图,我们进行 sendFile 系统调用时,数据被 DMA 引擎从文件复制到内核缓冲区,然后调用,然后掉一共 write 方法时,从内核缓冲区进入到 Socket,这时,是没有上下文切换的,因为在一个用户空间。
最后,数据从 Socket 缓冲区进入到协议栈。
此时,数据经过了 3 次拷贝,3 次上下文切换。
那么,还能不能再继续优化呢? 例如直接从内核缓冲区拷贝到网络协议栈?
实际上,Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Socket buffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。
具体如下图:
在这里插入图片描述
现在,index.html 要从文件进入到网络协议栈,只需 2 次拷贝:第一次使用 DMA 引擎从文件拷贝到内核缓冲区,第二次从内核缓冲区将数据拷贝到网络协议栈;内核缓存区只会拷贝一些 offset 和 length 信息到 SocketBuffer,基本无消耗。

等一下,不是说零拷贝吗?为什么还是要 2 次拷贝?
答:首先我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是重复的(只有 kernel buffer 有一份数据,sendFile 2.1 版本实际上有 2 份数据,算不上零拷贝)。例如我们刚开始的例子,内核缓存区和 Socket 缓冲区的数据就是重复的。
而零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。

mmap 和sendFile 的区别

  1. mmap 适合小数据量读写,sendFile适合大文件传输

  2. mmap 需要4次上下文切换、3次数据拷⻉;sendFile 需要3次上下文切换、最少2次数据拷⻉。

  3. sendFile 可以利用DMA方式,减少CPU拷⻉,mmap 则不能(必须从内核拷⻉到socket 缓冲区)。

    在这个选择上:RocketMQ 在消费消息时,使用了mmap。kafka使用了sendFIle。


哪些地方会使用到零拷⻉技术

  • 场景:
    – 文件较大,读写较慢,追求速度
    – JVM内存不够,不能加载太大的数据
    – 内存宽带不够,即存在其他程序或线程存在大量的IO操作,导致带宽本来就小

  • 实现的技术:
    Java的NIO, Netty kafka


代码案例(NIO举例)

传统IO代码

  • 服务端:
public class TranditionServer {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(7777);
        while (true) {
            Socket socket = serverSocket.accept();
            DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
            byte[] bytes = new byte[1024];
            while (true) {
                int readCount = dataInputStream.read(bytes, 0, bytes.length);
                if (readCount == -1) {
                    break;
                }
            }
        }
    }
}

客户端:

public class TranditionClient {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 7777);

        DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());

        FileInputStream in = new FileInputStream(new File("/Users/Desktop/资料/2560x1600.png"));
        byte[] bytes = new byte[1024];
        long startTime = System.currentTimeMillis();
        long readCount = 0;
        long total = 0;
        while ((readCount = in.read(bytes)) >= 0) {
            total += readCount;
            dataOutputStream.write(bytes);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("发送总字节数:" + total + ",耗时:"+(endTime-startTime)+"ms");
        //释放资源
        dataOutputStream.close();
        socket.close();
        in.close();
    }
}

在这里插入图片描述

NIO零拷贝代码

服务端

public class NewIOServer {
    public static void main(String[] args) throws IOException {
        InetSocketAddress inetSocketAddress = new InetSocketAddress(7777);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket socket = serverSocketChannel.socket();
        socket.setReuseAddress(true);
        socket.bind(inetSocketAddress);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (true) {
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(true);
            int readCount = 0;
            while (readCount != -1) {
                readCount = socketChannel.read(byteBuffer);
                byteBuffer.rewind();
            }
        }

    }
}

客户端

public class NewIOClient {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 7777));
        socketChannel.configureBlocking(true);
        FileInputStream inputStream = new FileInputStream(new File("/Users/Desktop/资料/2560x1600.png"));
        long startTime = System.currentTimeMillis();
        FileChannel fileChannel = inputStream.getChannel();
        long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
        long endTime = System.currentTimeMillis();
        System.out.println("发送总字节数:" + (transferCount) + "耗时:"+(endTime - startTime) + "ms");
        //释放资源
        fileChannel.close();
        socketChannel.close();
    }
}

在这里插入图片描述


参考资料:
https://www.bilibili.com/video/BV1c54y1v7X1/?spm_id_from=333.788.videocard.4
https://www.bilibili.com/video/BV1DJ411m7NR/?spm_id_from=333.788.videocard.5
https://www.jianshu.com/p/275602182f39
https://www.cnblogs.com/sebastian-tyd/p/13581192.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面我来为您图解一下Linux DMA贝数据到用户态的零拷贝Zero-Copy)技术。 首先,我们需要了解什么是DMA(Direct Memory Access,直接内存访问)技术。DMA是一种数据传输技术,它允许外设直接访问系统内存,而无需CPU的干预,从而提高数据传输效率。 在Linux中,当我们需要将数据从内核态传输到用户态时,通常情况下需要经过两次数据贝。第一次是将数据从设备驱动程序复制到内核缓冲区,第二次是将数据从内核缓冲区复制到用户空间缓冲区。这两次数据贝会消耗大量CPU资源,影响系统性能。 为了解决这个问题,Linux内核引入了零拷贝技术。零拷贝技术是指数据传输时,数据不需要经过CPU的贝,而是直接从内核空间传输到用户空间。这样可以减少CPU的负担,提高系统性能。 下面是零拷贝技术的流程图: ![DMA零拷贝技术流程图](https://img-blog.csdn.net/20180908100849451?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JhY2tpbmcyMDE4/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/50) 1. 应用程序发起读取请求。 2. 系统调用将请求传递给驱动程序。 3. 驱动程序将请求转换为DMA请求,并向DMA控制器发出请求。 4. DMA控制器将数据从设备读入内核缓冲区中。 5. DMA控制器将数据从内核缓冲区中直接传输到用户空间缓冲区,无需经过CPU。 6. DMA控制器传输完成后,向驱动程序发送通知。 7. 驱动程序将传输完成的数据返回给应用程序。 通过零拷贝技术,可以减少CPU的贝次数,提高数据传输效率,从而提高系统性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值