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

宝家宝汇的零拷贝,你知道吗?

时间:2023-03-17 18:05:15 科技观察

本文的主题是零拷贝的技术要点!下面我们将从以下几个问题的角度,对零拷贝的技术点进行全方位的分析。毕竟好记性不如烂笔头。为什么我们有DMA技术?我们先看看没有DMA的情况。该技术之前的IO流程:1.CPU向磁盘系统发送相应的命令,然后返回2.磁盘系统收到命令,将数据放入磁盘系统内部缓冲区,然后产生中断命令3、CPU接收到Interrupt信号,停止当前的工作,然后将磁盘系统缓冲区中的数据读入自己的寄存器中,再将寄存器中的数据写入内存中。在此数据传输期间,CPU不能执行其他任务。画图帮助大家懂的聪明的朋友已经发现了缺点,就是在数据传输的过程中,CPU无法执行其他命令。我们知道CPU是一个中央处理器,性能这个东西是可以省下来的,能用就用。毕竟整机要用这家伙简单搬几个字符数据肯定没问题,但是如果大量的数据需要CPU搬,那就很糟糕了。于是,DMA技术诞生了,也就是直接内存访问技术DirectMemoryAccessDMA技术是指在IO设备和内存之间传输数据时,数据传输的工作全部交给DMA控制器,CPU不再参与在任何与数据传输有关的事务中,使CPU为空。让我们仔细看看使用DMA控制器的过程。1、当用户调用read时,操作系统先发起IO请求,请求读取数据到自己的内存缓冲区,然后进入阻塞。2.操作系统收到请求,将IO请求发送给DMA,然后CPU执行其他任务,DMA发送给磁盘3,磁盘收到IO请求,将数据放入自己的缓冲区,当磁盘系统缓冲区满时,发起中断commandtoDMA4,DMA接收到中断指令时,将磁盘缓冲区数据复制到内核缓冲区,不占用CPU5,DMA已经读取到足够的数据,向CPU6发送中断信号,CPU接收到DMA信号,知道数据准备好了,从内核copy数据在用户空间查看整个过程,发现CPU不再参与数据处理的工作,而是由DMA来完成。不过CPU在这个过程中也是必不可少的,因为CPU需要告诉DMA传输什么,传输到哪里。Controller这就像一家初创公司。老板一个人忙不过来,就招了个秘书。不过,秘书操作什么,怎么操作,还是要听老板指挥。早期糖尿病A只存在于主板上。现在由于I/O设备越来越多,对数据传输的要求也不同,所以每个I/O设备都有自己的DMA控制器对于传统的文件传输,我简单说一下用户空间和内核空间。比如我们将一个Java程序部署到Linux服务器上,我们可以认为JVM区域是用户空间,其余空间是内核空间,用户空间和内核空间对系统文件的操作权限是不同的。传统的文件传输工作原理:数据读写是从用户空间和内核空间来回复制,而内核空间的数据是从磁盘读取或写入代码通常如下,一般需要两次系统调用:read(file,tmp_buf,长度);写(套接字,tmp_buf,len);看看这两行代码做了什么。用户态和内核态的四次上下文切换,每次系统调用都要先从用户态切换到内核态,然后等待内核态完成任务,再切换回用户态。一次上下文切换需要几十纳秒几微秒,时间看似很短,但是在高并发场景下,这种时间会变得不可忽略,影响系统的性能。中间还有4次数据拷贝,其中两次是DMA拷贝,DMA技术是将IO设备优化到内核区,另外两次是通过CPU1拷贝用户缓冲区,第一次拷贝,磁盘上的数据通过DMA技术复制到操作系统的内核区2、第二次Copy,CPU将内核缓冲区数据复制到用户缓冲区3、第三次复制,CPU传输用户缓冲区数据tothekernelbuffer4,第四次拷贝,通过DMA技术将内核数据传输到网卡buffer中的问题:我们移动了一次数据,中间却拷贝了4次。过多的上下文切换和过多的数据复制会降低系统性能。因此,如果要提高文件传输的性能,就需要减少用户态和内核态的上下文切换,优化内容副本的数量,减少用户态和内核态的上下文切换。该过程需要交给操作系统的内核来完成。一次系统调用意味着必须发生两次上下文切换。首先从用户态切换到内核态,待内核态完成任务后再切换到用户态执行相应的进程。因此,要减少上下文切换的次数,就需要减少系统调用的次数,减少数据副本的次数。数据传输复制了4次,其中内核复制到用户缓冲区,然后从用户缓冲区复制到内核缓冲区。这两个过程是不必要的,因为在文件传输的应用场景中,我们不会对用户空间的数据进行重新处理,所以这个数据不需要移动到用户空间。如何实现零拷贝?零拷贝技术的实现方式通常有2种类型:mmap+write(三份+两次系统调用)Sendfile(三份+一次系统调用)先说说他们是如何减少我们前面的“上下文切换”和“数据复制”的次数mmap+write我们知道read()系统调用会将内核缓冲区中的数据复制到用户缓冲区中,所以为了减少这一步的开销,我们可以将read()系统调用函数替换为mmap()。buf=mmap(file,len);write(sockfd,buf,len);mmap()系统调用函数将内核缓冲区中的数据直接“映射”到用户空间,从而使操作系统内核和用户空间将不需要任何进一步的数据复制操作。具体过程如下:1、应用程序调用mmap(),DMA将磁盘数据复制到内核缓冲区。这时,应用进程和内核就会共享内核缓冲区。2、应用系统调用write(),操作系统直接将内核缓冲区的数据复制到网络缓冲区,也属于内核态,内核中的复制由CPU操作。3、第三次拷贝,通过DMA技术将网络缓冲区的数据拷贝到网卡的缓冲区中,我们可以得到已知使用mmap()代替read(),可以减少一次数据拷贝的过??程。但这并不是理想的零拷贝,因为CPU仍然需要将内核缓冲区中的数据拷贝到socket缓冲区中,还需要4次上下文切换,因为系统调用还是2次。在Linux内核2.1版本中,Sendfile提供了一个专门用于发送文件的系统调用函数sendfile()。函数形式如下:#includessize_tsendfile(intout_fd,intin_fd,off_t*offset,size_tcount);它的前两个参数分别是目标和源的文件描述符,后两个参数是源的偏移量和复制数据的长度,返回值是实际复制数据的长度。首先,它可以替代之前的两个系统调用,read()和write(),这样可以减少一次系统调用,也减少了两次上下文切换的开销。其次,这个系统调用可以直接将内核缓冲区中的数据复制到套接字缓冲区,而不是复制到用户态,所以只有2次上下文切换和3次数据拷贝。如下图所示,但这并不是真正的零拷贝技术。如果网卡支持SG-DMA(TheScatter-GatherDirectMemoryAccess)技术(区别于普通的DMA),我们可以进一步减少CPU存放在内核缓冲区中的数据量。将数据复制到套接字缓冲区的过程。您可以在您的Linux系统中使用以下命令来检查网卡是否支持scatter-gather特性:$ethtool-keth0|grepscatter-gatherscatter-gather:on因此,从Linux内核2.4版本开始,对于支持SG的网卡,支持DMA技术的情况下,sendfile()系统调用的过程发生了一些变化。具体过程如下:1.DMA直接将磁盘上的数据复制到内核缓冲区2.将缓冲区描述符和数据长度传送到socket缓冲区,这样网卡的SG-DMA控制器就可以直接复制内核缓冲区中的数据到网卡的缓冲区。这个过程不需要将数据从操作系统内核缓冲区复制到套接字缓冲区,从而减少因此,在这个过程中,只做了2次数据复制,如下图所示:这就是所谓的零-拷贝(Zero-copy)技术,因为我们不在内存层面拷贝数据,也就是说,没有CPU移动数据,所有数据都通过DMA传输。CPU参与其中,但并未完全参与。DMA操作需要CPU命令,描述符和数据长度需要CPU发送零拷贝技术的文件传输方式。与传统文件传输方式相比,上下文切换次数和数据拷贝次数减少2倍。完成文件传输只需要两次上下文切换和数据拷贝次数,并且两次数据拷贝过程不需要经过CPU,两次由DMA承载。我们通常所说的零拷贝技术中的零指的是内核态和用户态之间的拷贝数变为0。所以,总体来说,零拷贝技术至少可以将文件传输的性能提高一倍。多于。PageCache的第一步是将磁盘文件数据复制到内核缓冲区。这个内核缓冲区就是磁盘高速缓冲区PageCache。内存速度比磁盘速度快,但内存空间比磁盘小。这个时候我们需要把热点数据放到缓存中,因为最近需要频繁访问,当空间不够用的时候,我们应该淘汰掉那些访问频率低的数据缓存。大家应该明白,零拷贝也是用到了缓存技术。这个时候先在PageCache里找,找到了直接返回,找不到就去磁盘读取,然后缓存到PageCache里。还有一点。读取磁盘数据时,需要找到数据所在的位置,但是对于机械磁盘来说,就是将磁头旋转到数据所在的扇区,然后开始“依次”读取数据,但是物理动作旋转磁头非常耗时。为了减少它的影响,PageCache使用了“预读功能”。例如,假设read方法一次只读取32KB字节。虽然read一开始只读了0-32KB字节,但是内核也会把后面的32-64KB字节读入PageCache,这样后面读取32-64KB的开销就很低了。如果进程在从PageCache淘汰之前读取了32-64KB,收益会非常大。因此,PageCache的优势主要有两个:缓存最近访问过的数据;预读功能;这两种方法将大大提高读写磁盘的性能。但是在传输大文件(GB级别的文件)时,PageCache就不起作用了,会多浪费一次DMA做的数据拷贝,导致性能下降。即使使用了PageCache的零拷贝,性能也会有所损失。直接占用一个大文件,导致一些热点小文件无法使用,性能下降。所以对于大文件的传输,不应该使用PageCache,也就是不应该使用零拷贝技术,因为PageCache可能会被大文件阻塞。这样一来,“热点”小文件就无法在PageCache中使用,在高并发环境下会造成严重的性能问题。对于大文件传输,可以采用异步IO和IO绕过PageCache来替代零拷贝技术。在nginx中,我们可以通过如下配置,根据文件的大小使用不同的方法:location/video/{sendfileon;aio开启;directio1024m;}当文件大小大于directio值时,使用“异步I/O+直接I/O”,否则使用“零拷贝技术”。总结1.早期IO,IO需要拷贝内核数据,2次系统调用,4次上下文切换,4次数据拷贝,CPU拷贝数据的过程中不能执行其他命令。2.DMA技术的引入,DMA可以代替CPU进行磁盘到磁盘内核区数据的拷贝,CPU可以在这期间执行其他命令,提高了性能数据拷贝到用户区,因为两者共享内核区缓冲区4.零拷贝??技术:Sendfile,1次系统调用,2次上下文切换,3次数据拷贝,直接指定原文件和目标文件,而不是原来的两次系统调用,直接完成一次5.真正的零拷贝技术:网卡支持SG-DMA技术,数据从磁盘系统读取到内核缓冲区后,不需要再复制到相应的socket缓冲区。是的,您只需要发送描述符和数据长度。这期间,你经历了1次系统调用,2次上下文切换,2次数据拷贝。内核级别没有数据副本。6、零拷贝技术是指PageCache缓存技术,缓存技术用于加快热点文件的查询速度,但不适用于大文件。大文件可以用异步IO和绕过PageCache的IO代替。参考资料:https://zhuanlan.zhihu.com/p/258513662本文转载自微信公众号“作尔君”,可通过以下二维码关注。转载本文请联系左尔君公众号。