Netty笔记1:线程模型
Netty笔记2:零拷贝
Netty笔记3:NIO编程
Netty笔记4:Epoll
Netty笔记5:Netty开发实例
Netty笔记6:Netty组件
Netty笔记7:ChannelPromise通知处理
Netty笔记8:ByteBuf使用介绍
Netty笔记9:粘包半包
Netty笔记10:LengthFieldBasedFrameDecoder很简单
Netty笔记11:编解码器
Netty笔记12:模拟Web服务器
Netty笔记13:序列化
前言
零拷贝技术的原理可以更好的理解NIO的操作,以及在编程时对编码的理解以及应用,是非常重要的一个知识点。
零拷贝
首先,我们要了解什么是“内核态”,什么是“用户态”。
**用户态:**不能直接操作系统硬件,即不能直接使用系统资源,它只能访问用户程序的空间资源。
**内核态:**可以直接使用系统资源,运行和保证系统程序稳定运行。
所以用户程序在操作文件时,都是通过内核态操作,那么数据交互就会在用户态和内核态直接来回切换,而这个过程中,都需要CPU的调度、任务分配以及管理等操作。
DMA
**零拷贝技术,核心点就是减少CPU的步骤,**为此出现了DMA
(直接存储器),由该部件管理IO操作,它是独立的指令集,不会占用CPU资源;
虽然DMA
已减少CPU的操作,提高了整个数据交互的速度,但是由于在数据交换过程中,都需要通过数据总线,当数据IO操作过多,数据总线的上限就限制了整个系统的性能,之后便引入了channel
,它跳过了数据总线之间的过程,直接在内核态空间中进行交换,使得性能进一步提升;
下图是传统的数据传送机制:
4次上下文文切换,4次拷贝:
用户态切换至内核态(读取数据)
内核态切换至用户态态(将缓冲区数据拷贝到用户态缓冲区)
用户态切换至内核态(将用户态缓冲区数据拷贝到硬件)
最终切换到用户态
内核态数据 - 拷贝到 - 内核态缓冲区
内核态缓冲区 - 拷贝到 - 用户态缓冲区
用户态缓冲区 - 拷贝到 - 内核态缓冲区
内核态缓冲区 - 拷贝到 - 硬件设备
mmap
mmap
是解决用户态和内核态切换问题的一个方案:
在内核态复制到用户态时,它不直接复制数据了,而是复制文件缓冲区的信息,包含文件大小、内存起始地址等,之后由用户态复制到内核态这步,,就不再由用户态进行复制了,而是从内核态进行复制,减少了一次用户态的拷贝,和CPU调度;
注意:mmap
映射的是文件缓冲区的信息,并非磁盘文件本身。
4 次上下文切换,3次拷贝;
特点:
- 用户态参与只复制文件信息
- 数据复制3次;
这种方式它只适合小文件,不大于2G;
JAVA中,它有一个类DirectByteBuffer
的类,它其实就是mmap
的实现,它映射了一个堆外内存,JVM中只保存了一个内存地址,实际的数据是保存在系统内存中的,所有对数据的读写操作,都通过unsafe
类直接交由内核完成。
sendfile
它不直接拷贝文件,而是只拷贝一个带有文件位置和长度等信息的文件描述符FD,也省去了用户态对文件信息的拷贝,它整个拷贝步骤没有用户态参与,内核态直接复制文件描述FD,但真正实际的数据写入到磁盘或者是传递到网络都是从源头那边直接复制的。
LINUX给每个文件都定义了一个“文件描述符FD”,它是一个int型的数字,文件的定义包括键盘、鼠标、网卡驱动、磁盘、文件等;
- 省去了用户态的参与,如果DMA不支持
sendfile
机制,就需要CPU的参与; - 只有2次数据复制(如果DMA支持sendfile机制,就是2次拷贝);
SPLICE
在Linux 2.6.17 后支持SPLICE,它不需要硬件支持,也不需要CPU,就可以完成数据的拷贝。
直接与写入的缓冲区建立管道pipe
,使他们存在于同一个缓冲区,省去CPU拷贝,所以,一共进行了2次上下文切换,2次拷贝。
4次上下文文切换,4次拷贝:
用户态切换至内核态(读取数据)
内核态切换至用户态态(将缓冲区数据拷贝到用户态缓冲区)
用户态切换至内核态(将用户态缓冲区数据拷贝到硬件)
最终切换到用户态
内核态数据 - 拷贝到 - 内核态缓冲区
内核态缓冲区 - 拷贝到 - 用户态缓冲区
用户态缓冲区 - 拷贝到 - 内核态缓冲区
内核态缓冲区 - 拷贝到 - 硬件设备