Netty-2零拷贝

零拷贝基本介绍

内核空间和用户空间

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

		File file = new File("D://test.txt");
        RandomAccessFile raf = new RandomAccessFile(file,"rw");

        byte[] bytes = new byte[(int) file.length()];
        // 1 读
        raf.read(bytes);
        Socket socket = new ServerSocket(8080).accept();
        // 2 写
        socket.getOutputStream().write(bytes);

IO流读文件

当读取文件时,首先会从用户态切换内核态,读取磁盘到内核空间(DMA copy:直接内存拷贝);
然后再将数据读到用户空间,此时又会从内核态切换到用户态,所以需要2次切换,2次 copy.

IO流写文件

当写时,首先将文件写到socker buffer ,会从用户态切换到内核态,
再从socker buffer,写道协议栈中,完成后会切换会用户态。也需要2次切换,2次 copy.

完整流程:
在这里插入图片描述
总结:在上述流程中,两次CPU copy显然时多余的,对此提出优化。零拷贝是网络编程的关键,很多性能优化都离不开。在Java程序中,常用的零拷贝有mmap(内存映射)和 sendfile

虚拟内存

现代操作系统使用虚拟内存,即虚拟地址取代物理地址,使用虚拟内存可以有2个好处:

  • 虚拟内存空间可以远远大于物理内存空间
  • 多个虚拟内存可以指向同一个物理地址

正是多个虚拟内存可以指向同一个物理地址,可以把内核空间和用户空间的虚拟地址映射到同一个物理地址,这样的话,就可以减少IO的数据拷贝次数啦

mmap 内存映射优化

mmap正是才有虚拟内存技术,不再将数据复制到用户空间,直接对内核空间的数据进行操作
在这里插入图片描述
总结:用户空间可以共享某块内核空间,减少了一次copy

sendfile优化

linux 2.1提供,实现原理是数据不经过用户态,直接从内核空间copy到socket buffer;
linux 2.4优化,避免了从内核空间copy到socket buffer,而只是将描述(长度/偏移量等)copy到socket buffer

sendfile表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作,因此可以使用它来实现零拷贝。
在这里插入图片描述
总结:一共需要经历 3次 copy, 2次切换。

mmap与sendfile的区别

  1. mmap适合小数据量读写, sendfile适合大文件传输
  2. mmap需要4次上下文切换,3次数据拷贝; send File需要3次上下文切换,最少2次数据拷贝
  3. sendfile可以利用DMA方式,减少CPU拷贝,mmap则不能

NIO零拷贝使用

transferTo : 基于sendfile,DMA引擎直接把数据从内核缓冲区传输到协议引擎,从而消除了最后一次CPU
copy。经过上述过程,数据只经过了2次copy就从磁盘传送出去了。实现了真正的Zero-Copy

// 服务端
public class NioFileServer {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 创建 serverSocketChannel 并绑定端口
        serverSocketChannel.socket().bind(new InetSocketAddress(7001));
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (true) {
            SocketChannel client = serverSocketChannel.accept();
            int count = 0;
            while (-1 != count) {
                try {
                    count = client.read(buffer);
                } catch (IOException e) {
                    break;
                }
                // 倒带 就是复用
                buffer.rewind();
            }
        }
    }
}
// 客户端
public class NioFileClient {
    public static void main(String[] args) throws IOException {
        SocketChannel channel = SocketChannel.open();
        channel.socket().connect(new InetSocketAddress("127.0.0.1",7001));
        String filePath = "H:\\资料\\书籍\\Java并发编程之美.pdf";
        FileChannel fileChannel = new FileInputStream(filePath).getChannel();
        long start = System.currentTimeMillis();
        // linux 下调用 transferTo 就可以完成
        // window 下调用 transferTo 一次最多 8M ,所以需要分段传输,注意每次的传输位置
        long fileSize = fileChannel.size();
        System.out.println("原始文件大小: "+fileSize);
        int pageSize = 8 * 1024 * 1024 ; // 8M
        int pageIndex = 0;
        long transCount = 0 ;
        while (true){
            int i = pageIndex * pageSize;
            if (i < fileSize) {
                transCount += fileChannel.transferTo(i, pageSize, channel);
                pageIndex ++;
                System.out.printf("第%d 批 ",pageIndex);
            }else {
                break;
            }
        }
        System.out.println();
        System.out.println("传输文件大小:"+transCount +" 耗时:" + (System.currentTimeMillis() - start));
        fileChannel.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值