当前位置: 首页 > 科技观察

AliErmian:什么是Mmap?

时间:2023-03-14 14:44:48 科技观察

面试中经常会遇到的问题是:RocketMQ为什么快?为什么卡夫卡快?什么是mmap?有一点绕不开这类题就是零拷贝,虽然还有一些其他的原因,但是今天我们的话题主要是零拷贝。传统IO在开始讲零拷贝之前,我们首先要有一个传统IO方式的概念。基于传统的IO方式,底层实际上是通过调用read()和write()来实现的。通过read()将数据从硬盘读取到内核缓冲区,然后复制到用户缓冲区;然后通过write()写入socketbuffer,最后写入网卡设备。整个过程中,用户态和内核态之间有4次上下文切换,4次拷贝。具体过程如下:用户进程通过read()方法向操作系统发起调用。此时上下文从用户态转移到内核态DMA控制器将数据从硬盘复制到读缓冲区CPU将读缓冲区数据复制到应用程序缓冲区,上下文从内核态变为用户态,read()返回到用户进程并通过write()方法发起调用,上下文从用户态变为内核态,应用程序缓冲区中的CPU将socket缓冲区中的数据复制到socket缓冲区中。DMA控制器会将套接字缓冲区中的数据复制到网卡中。上下文将从内核态切换到用户态,write()将返回。它是什么?什么是上下文切换?简单的说,用户空间就是指用户进程的运行空间,内核空间就是内核的运行空间。如果进程运行在内核空间,就是内核态,如果运行在用户空间,就是用户态。出于安全原因,它们相互隔离,用户态和内核态之间的上下文切换非常耗时。从上面我们可以看出,一个简单的IO过程会产生4次上下文切换,在高并发场景下无疑会对性能产生较大的影响。那么什么是DMA拷贝呢?因为对于一个IO操作来说,是通过CPU发出相应的指令来完成的,但是与CPU相比,IO的速度太慢了,CPU要花费大量的时间来等待IO。状态。因此,产生了DMA(DirectMemoryAccess)直接内存访问技术。本质上,它是主板上一块独立的芯片,通过它进行内存和IO设备之间的数据传输,从而减少CPU的等待时间。但是不管是谁在复制,频繁的复制耗时也是对性能有影响的。Zero-copy零拷贝技术是指计算机在执行操作时,CPU不需要先将数据从某个内存拷贝到另一个特定区域。该技术通常用于在通过网络传输文件时节省CPU周期和内存带宽。所以对于零拷贝来说,并不是真的完全没有数据拷贝的过??程,只是为了减少用户态和内核态的切换次数和CPU拷贝的次数。这里,就针对性的说说几种常见的零拷贝技术。mmap+writemmap+write只是简单的用mmap代替了read+write中的read操作,减少了CPU的一份拷贝。mmap的主要实现是将读缓冲区的地址和用户缓冲区的地址进行映射,共享内核缓冲区和应用程序缓冲区,从而减少CPU从读缓冲区到用户缓冲区的一次拷贝。整个过程中,用户态和内核态的上下文切换有4次,副本3次。具体过程如下:用户进程通过mmap()方法向操作系统发起调用,上下文从用户态变为内核态。DMA控制器将数据从硬盘的上下文从内核态复制到用户态,mmap调用返回到用户进程。调用由write()方法发起,上下文从用户态切换到内核态。CPU将readbuffer中的数据复制到socketbuffer中DMA控制设备将socketbuffer中的数据复制到网卡中,将context从内核态切换到用户态,write()返回mmap保存一个CPU副本。同时,由于用户进程中的内存是虚拟的,只映射到内核的读Buffer,所以可以节省一半的内存空间,更适合大文件的传输。与mmap相比,sendfile也减少了一次CPU拷贝,同时也减少了两次上下文切换。sendfile是Linux2.1内核版本之后引入的系统调用函数。通过使用sendfile可以直接在内核空间传输数据,从而避免了用户空间和内核空间的拷贝,节省了一次系统调用,即两次上下文切换。整个过程中有2次用户态和内核态的上下文切换,3次拷贝。具体过程如下:用户进程通过sendfile()方法向操作系统发起调用,上下文从用户态切换到内核态。DMA控制器将数据从硬盘中读取出来,CPU将readbuffer中的数据复制到socketbuffer中,DMA控制器将socketbuffer中的数据复制到网卡中。上下文从内核态切换到用户态,sendfile调用将sendfile方法的IO数据返回给用户态。它是完全不可见的,因此只适用于根本不需要用户空间处理的情况,比如静态文件服务器。sendfile+DMAScatter/Gather在Linux2.4内核版本之后,进一步优化了sendfile。通过引入新的硬件支持,这种方法称为DMAScatter/Gather分散/聚集功能。它将读缓冲区中的数据描述信息——内存地址和偏移量记录到socket缓冲区中,DMA根据这些将数据从读缓冲区复制到网卡中,相比于CPU复制整个过程减少了一次之前的版本有两次用户态和内核态的上下文切换,进程中有两份副本,更重要的是根本没有CPU副本。具体过程如下:用户进程通过sendfile()方法向操作系统发起调用,上下文从用户态变为内核态DMA控制器使用scatter将数据从硬盘复制到读缓冲区。离散存储CPU将读取缓冲区中的文件描述符和数据长度发送到套接字缓冲区。DMA控制器根据文件描述符和数据长度使用分散。/gather将数据从内核缓冲区复制到网卡sendfile()调用返回,上下文从内核态切换到用户态DMAgather和sendfile一样,数据对用户空间是不可见的,需要硬件支持,输入的文件描述符只能是文件,但是进程中没有CPU拷贝过程,大大提高了性能。应用场景针对文章开头提到的两种场景:RocketMQ和Kafka都使用了零拷贝技术。对于MQ来说,无非是生产者将数据发送给MQ并持久化到磁盘,然后消费者从MQ中读取数据。对于RocketMQ来说,这两步使用的是mmap+write,而Kafka使用的是mmap+write持久化数据,使用sendfile发送数据。总结由于CPU和IO速度之间的差异,创建了DMA技术以通过DMA处理减少CPU等待时间。传统的IOread+write方式会产生2个DMA副本+2个CPU副本,同时产生4次上下文切换。mmap+write方式产生2个DMA拷贝+1个CPU拷贝,4次上下文切换,通过内存映射减少1次CPU拷贝,可以减少内存占用,适用于大文件传输。sendfile方法是新加入的系统调用函数,产生2个DMA拷贝+1个CPU拷贝,但只有2次上下文切换。因为只有一次调用,减少了上下文切换,但用户空间对IO数据不可见,适用于静态文件服务器。sendfile+DMAgather方式产生2个DMA拷贝,没有CPU拷贝,只有2次上下文切换。虽然性能有了很大的提升,但是需要依赖新的硬件设备支持。参考:https://juejin.cn/post/6844903949359644680#heading-19https://www.cnblogs.com/xiaolincoding/p/13719610.htmlhttps://time.geekbang.org/column/article/118657https://www.toutiao.com/i6898240850917114380/本文转载自微信公众号「爱小仙」,可关注下方二维码。转载本文请联系艾小仙公众号。