Direct Memory Access , 直接内存访问,是一种无须cpu的参与就可以让外设与系统内存进行双向数据传输的硬件机制。
DMA拷贝使系统CPU从实际的IO数据传输中解脱出来,从而大大提高系统吞吐率。在数据传输期间,cpu可以并发的执行其他任务。
传统的IO数据拷贝需要经历4次copy 和 3次状态切换
四次copy:
1、由硬件 经过DMA ——> 内核buffer
2、内核buffer ——> 用户buffer
3、用户buffer ——> socket buffer
4、socket buffer ——> protocol engine 协议栈
三次切换:
1、用户态 ——> 内核态 (用户上下文 ——> 内核上下文)
2、内核态 ——> 用户态
3、用户态 ——> 内核态
传统的IO数据传输,经历了4次数据拷贝,而在这里面从 内核缓冲区 到 用户缓冲区 , 再从用户穿冲区到 socket缓冲区,这个过程是没有必要的。零拷贝就是为了解决这个问题 ,零拷贝指的是零CPU拷贝 , DMA拷贝是无法避免的。
实现技术:
1、内存映射 mmap
2、sendFile
并没有真实的改变传统的IO流程,只不过是使 内核buffer 和 用户buffer 共享了同一块内存,所以相对来讲减少了一次数据拷贝。
mmap通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核空间的数据。这样就减少了一次数据copy。
三次数据copy:
1、由硬件 经过DMA ——> 内核buffer
2、内核buffer(用户buffer 共享了一块内存) ——> socket buffer
3、用户buffer ——> socket buffer
两次切换:
1、用户态 ——> 内核态 (用户上下文 ——> 内核上下文)
2、内核态 ——> 用户态
内存映射共经历了3次数据copy 和 2次状态切换。 并没有实现领copy , 只是减少了copy的次数
从linux 内核2.4开始,对于网卡支持SG-DMA技术的情况下,sendFile()系统调用,过程如下:
1、通过DMA将数据拷贝至内核缓冲区
2、缓冲区描述符和数据穿度 copy到socket缓冲区,这个数据量很小,消耗比较低
3、网卡SG-DMA控制器 ,将内核缓冲区copy至 协议栈
4.1、接收和发送byteBuffer ,使用直接内存进行socket读写,减少了直接内存到堆内存的数据copy
4.2、文件传输调用FileRegion包装的transferTo,直接将文件缓冲区数据发送到目标chuannel , 避免内存copy
4.3、提供了CompositeBytebuf , 将多个buff合并成一个逻辑上的buff
4.4、通过wrap操作将byte数组 、ByteBuf、ByteBuffer 转换为一个byteBuf对象 ,减少内存copy
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
byteBuf对象和 源bytes 共享一块内存,避免copy
4.5、支持slice操作,将一个byteBuf 分解为多个共享一个存储区域的byteBuf,避免内存copy