我知道,如果不了解底层技术,就如同空中楼阁,再这样下去,阿里P10就没有希望了.想到这里,我开始慌了,所以今天就和大家一起学习一个底层的技术点:Zero-CopyZero-Copy。图片来自PexelsLinux系统。一切都是文件。仔细想想,Linux系统中的很多活动无非就是读写操作。零拷贝似乎可以提高读写性能。废话不多说,直接开大车出发!数据拷贝的基本过程受限于Linux系统内部缓存和内存容量,更多的数据存储在磁盘中。对于web服务器来说,往往需要从磁盘读取数据到内存,再通过网卡传输给用户:上面说的数据流只是一个大框架,我们来看几种模式:①CPU-only模式如上图所示:当应用程序需要读取磁盘数据时,从用户态调用read()到内核态,read()系统调用最终由CPU完成。CPU向磁盘发起I/O请求,磁盘收到数据后开始准备数据。磁盘将数据放入磁盘缓冲区后,向CPU发起I/O中断,报告CPU数据已准备好。CPU收到磁盘控制器的I/O中断后,开始复制数据,read()完成后返回,然后从内核态切换到用户态。②CPU&DMA模式CPU的时间是宝贵的,让它去做杂事是一种资源浪费。直接内存访问(DirectMemoryAccess)是硬件设备绕过CPU,直接独立访问内存的一种机制。因此,DMA在一定程度上解放了CPU,让硬件直接去做之前CPU的杂活,提高了CPU的效率。目前支持DMA的硬件包括:网卡、声卡、显卡、磁盘控制器等。有了DMA的参与,流程发生了一些变化:主要变化是CPU不再直接与磁盘交互,但DMA与磁盘交互,将数据从磁盘缓冲区复制到内核缓冲区,后续过程类似。敲黑板:无论从CPU-only模式还是DMA&CPU模式,都存在多次冗余数据拷贝和内核态与用户态的切换。我们继续思考web服务器读取本地磁盘文件的数据,然后通过网络传输给用户的详细过程。普通模式数据交互一次完成的数据交互包括几个部分:系统调用syscall、CPU、DMA、网卡、磁盘等。系统调用syscall是应用程序和内核之间的桥梁。每次调用/返回都会导致两次切换:调用系统调用从用户态切换到内核态。系统调用返回,从内核模式切换到用户模式。我们来看完整的数据拷贝过程图:读数据过程:应用程序需要读取磁盘数据,调用read()函数从用户态切换到内核态。这是第一个状态开关。DMA控制器将数据从磁盘复制到内核缓冲区,这是第一次DMA复制。CPU将数据从内核缓冲区复制到用户缓冲区,这是第一次CPU复制。CPU完成拷贝后,read()函数返回,从用户态切换到用户态,即第二次状态切换。数据写入过程:应用程序需要向网卡写入数据,调用write()函数从用户态切换到内核态。这是第一个开关。CPU将用户缓冲区数据复制到内核缓冲区,这是CPU的第一次复制。DMA控制器将数据从内核缓冲区复制到套接字缓冲区,这是第一次DMA复制。拷贝完成后,write()函数返回,从内核态切换到用户态,即第二次切换。总结一下:读过程涉及2次空间切换,1次DMA拷贝,1次CPU拷贝。写入过程涉及2次空间切换,1次DMA拷贝,1次CPU拷贝。可以看出,在传统模式下,涉及多个空间切换和数据冗余副本,效率不高。接下来,零拷贝技术将发挥作用。我们可以看到零拷贝技术出现的原因。如果应用程序不修改数据,就会从内核缓冲区转到用户缓冲区,再从用户缓冲区转到内核缓冲区。两次数据拷贝都需要CPU的参与,涉及用户态和内核态的多次切换,增加了CPU的负担。我们要减少冗余的数据拷贝,解放CPU,这就是零拷贝Zero-Copy技术。解决方案目前零拷贝技术的几种实现方式有:mmap+write、sendfile、sendfile+DMAcollection、splice等。读取内核中的缓冲区和用户空间中缓冲区的地址,从而实现内核缓冲区和用户缓冲区的共享。这样就减少了用户态和内核态的CPU拷贝,但是内核空间还是有CPU拷贝的。mmap对于大文件传输有一定的优势,但是小文件可能会出现碎片,多个进程同时操作文件时可能会产生coredump信号。②sendfile方式和mmap+write方式有一些改进,但是系统调用带来的状态切换并没有减少。sendfile系统调用是Linux内核2.1版本引入的,它在两个文件之间建立传输通道。sendfile方法只用一个函数就完成了前面的read+write和mmap+write函数,省去了2次状态切换。由于数据不经过用户缓冲区,因此无法修改数据。从图中可以看出,应用程序只需要调用sendfile函数就可以完成,而且只有2次状态切换,1次CPU拷贝,2次DMA拷贝。但是sendfile在kernelbuffer和socketbuffer中还是有CPU拷贝的,也许这个可以优化一下。③sendfile+DMAcollectionLinux2.4内核优化了sendfile系统调用,但需要硬件DMA控制器的配合。升级后的sendfile将内核空间缓冲区中相应的数据描述信息(文件描述符、地址偏移量等)记录到socket缓冲区中。DMAcontroller根据socketbuffer中的地址和offset,将数据从kernelbuffer中复制到网卡中,这样在kernelspace中只节省了一份CPU拷贝。该方法有2次状态切换,0次CPU拷贝,2次DMA拷贝,但仍然不能修改数据,需要硬件层面DMA的支持,而sendfile只能将文件数据拷贝到socket描述符中。某些限制。④splice模式splice系统调用是Linux在2.6版本中引入的。不需要硬件支持,不再局限于套接字,实现两个普通文件之间的零拷贝数据。splice系统调用可以在kernelbuffer和socketbuffer之间建立管道传输数据,避免了两者之间的CPU拷贝操作。splice也有一些限制,因为它的两个文件描述符参数之一必须是管道设备。总结本文通过介绍数据交互的基本流程和传统模型的不足,介绍了零拷贝的一些实现方法。零拷贝技术是一种非常底层且重要的读写优化,对提高业务的并发度有很大的帮助。就这样。下次见!作者:后端技术罗盘编辑:陶佳龙来源:转载自公众号后端技术罗盘
