【Linux编程】大冒险之零拷贝技术探究

时间:2020-11-30 04:43:09

对于网络数据传输或I/O数据拷贝而言,零拷贝技术主要指的是避免内核缓冲区和用户缓冲区中的不必要的数据拷贝操作。

Linux传统I/O

Linux传统I/O操作是一种缓冲I/O,在数据传输中,操作系统会将 I/O 的数据缓存在文件系统的页缓存中,即操作系统内核缓冲区中。
比如:在网络中传输一个文件时,发送端应用程序会先检查内核缓冲区中有没有需要发送的这个文件的数据,如果没有,则会将这个文件从磁盘拷贝到内核缓冲区中,然后再从内核缓冲区拷贝到应用程序的用户缓冲区,如果应用程序不对数据进行处理或处理完毕之后,再将文件拷贝到内核中的socket发送缓冲区(比如TCP发送缓冲区),待内核socket缓冲区中有足够的数据时,就会把数据发送到网卡上,然后在网络上进行传输。其数据传输过程如下图所示:

【Linux编程】大冒险之零拷贝技术探究

其过程至少发生了四次数据的拷贝,其频繁的读写对CPU的使用和内存的带宽开销是非常大的。

零拷贝技术

零拷贝技术相对传统I/O技术来说,主要是避免数据传输过程中频繁的数据拷贝操作,提高传输效率,并且使CPU有更多时间执行其它任务。

零拷贝技术分类

  • 直接I/O机制:

通过上面的介绍,普通I/O(即缓冲I/O)会被内核缓存。相对于普通I/O机制,直接I/O机制对文件的访问不经过内核的缓存,数据直接在磁盘和应用程序地址空间进行传输。这就避免了内核缓冲区和用户缓冲区的数据拷贝,降低了读写操作对CPU的使用以及对内存带宽的占用。
但是直接 I/O 不能提供缓存 I/O 的优势。缓存 I/O 的读操作可以从高速缓冲存储器中快速获取数据,而直接 I/O 的读数据操作会造成磁盘的同步读,导致进程需要较长的时间才能执行完。

  • 不经过用户缓冲区

主要是指不需要将数据拷贝或者映射到应用程序地址空间中,直接在内核中传输。
比如sendfile( )系统调用:sendfile( ) 系统调用利用 DMA 将文件中的数据拷贝到操作系统内核缓冲区中,然后数据被拷贝到与 socket 相关的内核缓冲区中。接下来,DMA 将数据从内核 socket 缓冲区中拷贝到网卡中去。如果在用户调用 sendfile ( ) 系统调用进行数据传输的过程中有其他进程截断了该文件,那么 sendfile ( ) 系统调用会简单地返回给用户应用程序中断前所传输的字节数,errno 会被设置为 success。
其传输过程如下:

【Linux编程】大冒险之零拷贝技术探究

由于sendfile( )函数只能往socket上写数据,因此它几乎是专门为了在网络上传输文件而设计的。

DMA简介:Direct Memory Access(存储器直接访问)。这是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。整个数据传输操作在一个称为”DMA控制器”的控制下进行的。CPU除了在数据传输开始和结束时做一点处理外,在传输过程中CPU可以进行其他的工作。这样,在大部分时间里,CPU和输入输出都处于并行操作。因此,使整个计算机系统的效率大大提高。

  • 优化数据在内核缓冲区和用户缓冲区之间的传输

保留了传统的在用户应用程序地址空间和操作系统内核地址空间之间传递数据的技术,但却在传输上进行优化。
比如写时复制技术:如果多个应用程序同时访问同一块数据,但这块数据只有一份,那么可以为这些应用程序分配指向这块数据的指针,在每一个应用程序看来,它们都拥有这块数据的一份数据拷贝。若一个应用程序需要访问但不修改该数据时,直接读这个数据而不复制,但当应用程序需要对这块数据进行修改的时候,就需要将数据真正地拷贝到该应用程序的地址空间中去,也就是说,该应用程序拥有了一份真正的私有数据拷贝,对这份私有数据进行修改。这样做是为了避免该应用程序对这块数据做的更改被其他应用程序看到。这个过程对于应用程序来说是透明的,如果应用程序永远不会对所访问的这块数据进行任何更改,那么就永远不需要将数据拷贝到应用程序自己的地址空间中去。

以上只进行了比较浅的分析,知识有限,找了一些深入的资料没有看懂,比如其实现机理,等需要对Linux内核深入学习的时候在回头看,不过暂时理解这些对于网络编程已经有很大的帮助了。

相关资料
Linux 中的零拷贝技术第 1 部分
Linux 中的零拷贝技术第 2 部分