呢作为一个高性能的网络通信框架,被越来越多互联网公司关注和重视。最近,有小伙伴在面试过程中被问到Netty是如何实现零拷贝的问题?,今天,我给大家来聊一聊。另外,往期面试题解析中配套的文档我已经准备好,想获得的可以在我的煮叶简介中找到。
我们先来看什么是零拷贝?
1、什么零拷贝
在计算机中,完成数据传输,要么是通过网络,要么就是通过本地磁盘。通常完成一次完整I/O交互流程分为两阶段,首先拷贝到系统内核空间,由操作系统来完成;紧接着要拷贝到用户空间,这个由应用程序来完成,具体交互流程如下图所示。
那什么是零拷贝呢?顾名思义,零拷贝的意思包括两个部分,“零”和“拷贝”:
“拷贝”:就是指数据从一个存储区域转移到另一个存储区域。
“零” :表示次数为0,它表示拷贝数据的次数为0。
合起来“零拷贝”就是不需要将数据从一个存储区域复制到另一个存储区域。
2、为什么要零拷贝
零拷贝主要是指将系统内核空间的内存和用户空间的内存实现直接关联映射,从而省去了数据传输过程中的来回拷贝,也就是说,要完成数据传输数据拷贝次数为0次。
有这么一段代码,主要将服务端主机磁盘中的文件从已连接的socket发出去。关键实现代码如下:
while((n = read(diskfd, buf, BUF_SIZE)) > 0){ write(sockfd, buf , n); }
以上代码是用传统的IO编写的,它的执行过程大致是这样的:
首先,read()方法,会把数据从磁盘循环读取到内核缓冲区,再拷贝到用户缓冲区。
然后,write()方法先把数据写入到socket缓冲区,最后写入网卡设备。
因为,传统的IO在在数据传输过程中拷贝次数太多,导致性能低下。为了减少拷贝次数,增加系统性能,才有了零拷贝的设计。另外,零拷贝并不是指完全没有文件的拷贝,只是减少了拷贝的次数。
3、零拷贝在Netty中的实现
从流程图可以看出,传统IO的读写流程,包括了4次用户态和内核态的切换,也就是4次上下文切换,4次数据拷贝,其中两次CPU拷贝以及两次的DMA拷贝。关于上下文切换和DMA拷贝,在这里我们就不详细赘述了,以后可以单独拍摄一期视频来详细介绍。想了解的小伙伴可以在评论区回复666.
那在Netty中,是如何实现零拷贝的呢?有以下三种方式
1. 使用堆外内存,也叫直接内存。Netty的接收和发送都采用DIRECT BUFFERS,对应系统底层的mmap机制,直接使用堆外内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。
2. 提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户只需要像操作一个Buffer一样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer,不需要做内存拷贝。
3. 的文件传输采用了transferTo方法,直接使用了NIO的sendfile机制,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。