本文的主题是零拷贝的技术要点!下面我们将从以下几个问题的角度,对零拷贝的技术点进行全方位的分析。毕竟好记性不如烂笔头。为什么我们有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()。函数形式如下:#include
