什么是零拷贝?
在讲解零拷贝之前,我们先来了解一下为什么要引入零拷贝,以及零拷贝解决了什么问题。
内核态与用户态
操作系统为了限制不同程序之间的访问能力,防止他们获取别的程序的内存数据或者外设的数据以及限制某些指令的调用,所以划分出内核与用户两个权限等级。所有的用户程序都运行在用户态,但用户程序有时候可能需要从磁盘读取文件或者写入文件,这些操作只能由内核态完成,所以这个时候就需要内核态与用户态的切换以及数据的拷贝
假设我们的应用程序需要从磁盘中读取文件,然后通过网络发送出去,Linux 操作系统会基于数据排序或者校验等各方面因素的考虑,内核会在处理数据传输的过程中进行多次拷贝操作。这些数据拷贝操作会降低数据传输的性能。
当应用程序需要访问磁盘数据时,内核会使用DMA将数据从磁盘读出并放到内核缓冲区,再将数据拷贝到用户缓冲区,再将数据拷贝到socket缓冲区,再使用DMA将数据拷贝到网卡设备中。
拷贝流程
DMA是一种无需CPU的参与就可以让外设和系统内存之间进行双向数据传输的硬件机制。使CPU从实际的I/O数据传输过程中摆脱出来。
从上述流程可以看出,数据经过了多次的拷贝操作,数据拷贝需要占用CPU时间片,也经历了多次内核与用户态的转换,对性能影响较大。为了解决数据多次拷贝这个问题,Linux引入了零拷贝技术。
Linux mmap()
mmap用来代替Linux read接口来读取数据,调用之后,数据会通过DMA拷贝到操作系统内核缓冲区,接着用户程序与内核公用这个缓冲区,这样用户空间与内核空间就不需要互相拷贝数据了。
对应Java
Java对mmap的实现就是MappedByteBuffer类,使用它我们可以直接在内存中进行文件读写。
Linux sendfile()
sendfile接口利用DMA将数据拷贝到内核缓冲区,然后数据被拷贝到socket缓冲区,接下来DMA再将数据拷贝到网卡设备中。sendfile() 调用不需要将数据拷贝或者映射到应用程序地址空间中去,所以 sendfile() 只是适用于应用程序地址空间不需要对所访问数据进行处理的情况。
而在带有DMA收集拷贝的情况下:
sendfile() 系统调用利用 DMA 引擎将文件内容拷贝到内核缓冲区去;然后,将带有文件位置和长度信息的缓冲区描述符添加到 socket 缓冲区中去,此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中,DMA 引擎会将数据直接从内核缓冲区拷贝到网卡设备中。
通过这种方法,CPU 在数据传输的过程中不但避免了数据拷贝操作
对应Java
Java就sendfile的实现就是FileChannel的transferTo()和transferFrom(),将数据从一个channel传输到另一个channel
参考:
https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy1/index.html
https://www.ibm.com/developerworks/cn/linux/l-cn-zerocopy2/index.html
网友评论