在看Kafka相关设计时,注意到一个零拷贝技术。对其做一简要介绍:
为什么需要零拷贝技术?
以常见的read 和write函数为例:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
如上图所示:在这个读写过程中,一共涉及了4次复制和4次状态切换。
- read系统调用导致上下文从用户模式切换到内核模式。第一次复制由DMA引擎执行,DMA引擎从磁盘读取文件内容并将它们存储到内核地址空间缓冲区中
- 将数据从内核缓冲区复制到用户缓冲区,并且read系统调用返回。read调用返回导致上下文从内核切换回用户模式。现在数据存储在用户地址空间缓冲区中
- write系统调用导致上下文从用户模式切换到内核模式。执行第三次复制,以再次将数据放入内核地址空间缓冲区。这个时候,数据被放入一个不同的缓冲区,一个与sockets相关联的缓冲区。
- write系统调用返回,执行第四个上下文切换,将数据从内核缓冲区传递到协议引擎
可以看到在这个过程中,对数据进行了多次复制,同时上下文环境进行了频繁的切换,对于CPU造成巨大压力。我们需要一种技术,减少不必要的数据复制,让CPU解脱出来
什么是零拷贝技术?
零拷贝主要的任务就是避免CPU将数据从一块存储拷贝到另外一块存储,主要就是利用各种零拷贝技术,避免让CPU做大量的数据拷贝任务,减少不必要的拷贝,或者让别的组件来做这一类简单的数据传输任务,让CPU解脱出来专注于别的任务。这样就可以让系统资源的利用更加有效。引用了(https://www.jianshu.com/p/fad3339e3448)
常见的零拷贝技术
- 直接 I/O:对于这种数据传输方式来说,应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输:这类零拷贝技术针对的是操作系统内核并不需要对数据进行直接处理的情况,数据可以在应用程序地址空间的缓冲区和磁盘之间直接进行传输,完全不需要 Linux 操作系统内核提供的页缓存的支持。
- 避免数据在操作系统内核地址空间的缓冲区和用户应用程序地址空间的缓冲区之间进行拷贝。有的时候,应用程序在数据进行传输的过程中不需要对数据进行访问,那么,将数据从 Linux 的页缓存拷贝到用户进程的缓冲区中就可以完全避免,传输的数据在页缓存中就可以得到处理。在某些特殊的情况下,这种零拷贝技术可以获得较好的性能。Linux 中提供类似的系统调用主要有 mmap(),sendfile() 以及 splice()。
- 对数据在 Linux 的页缓存和用户进程的缓冲区之间的传输过程进行优化。该零拷贝技术侧重于灵活地处理数据在用户进程的缓冲区和操作系统的页缓存之间的拷贝操作。这种方法延续了传统的通信方式,但是更加灵活。在Linux 中,该方法主要利用了写时复制技术。