如何实现零拷贝

2022-12-01 21:24:11 浏览数 (2)

零拷贝经常在各个框架使用,比如kafka,rocketmq,都起到了很好的作用,首先我们要知道零拷贝不是没有一次拷贝,是尽可能的减少拷贝。

我们先看看传统的数据拷贝

  1. 应用程序调用read函数,向操作系统发送IO操作进行上下文切换,用户态到内核态的切换
  2. DMA控制器把数据从磁盘拷贝到内核缓冲区
  3. cpu从内核缓冲区拷贝到用户缓冲区,进行上下文切换内核态到用户态
  4. 用户程序调用write函数发起IO请求,进行上下文切换,用户态到内核态的切换
  5. cpu从用户缓冲区把数据拷贝到socket缓冲区
  6. DMA控制器从socket缓冲把数据拷贝到网卡中,进行上下文切换,然后write函数返回

上面过程一共进行了4次拷贝,4次上下文切换

上面有些概念简单说明一下

用户空间和内核空间

首先我们知道我们的程序是建立在操作系统之上的,但是我的程序往往会有些动作是非常危险的,比如读写磁盘,内存读写,因此操作系统就分成了两个空间,一个用户空间,一个内核空间

  • 用户空间,主要是让应用程序使用空间,是不允许直接操作内核空间的
  • 内核空间,只要做一些进程调度,内存分配,连接硬件资源等功能

上下文以及上下文切换

上下文,cpu寄存器是容量小,却速度快的一块内存,程序计数器,是记录cpu指令的东西,而我们cpu运行之前是要必须依赖cpu寄存器和程序计数器,这就是上下文

上下文切换,就是把上一个cpu的内容即寄存器和程序计算器保存起来,加载新的上下文,然后执行新的cpu任务,这就是上下文切换

虚拟内存,现在的操作系统都是使用虚拟内存,虚拟内存替代了真实的物理地址,多个虚拟内存可以指向同一个物理地址,我们可以把用户空间和内核空间的虚拟内存指向同一个物理地址,这样可以减少io操作的拷贝

DMA技术

DMA,全名Direct Memory Access,即直接内存访问,他简单来说就是帮助cpu干活的小弟,实际上他是一块对的芯片,可以允许外部设备和内存寄存器直接进行io数据传输,其过程不需要cpu参加,使用DMA技术是为了让cpu空闲起来,做其他事情,提高效率

实现零拷贝有三种方式

  • mmap write
  • sendfile
  • 带有DMA拷贝功能的sendfile

mmap write,前面我们说过虚拟内存,可以把用户空间和内核空间的虚拟内存映射到同一个物理地址,而mmap就是利用这个特性,把用户缓存区和内核缓存区进行了映射,达到减少拷贝的目的,所有操作都在内存中完成

  1. 应用程序调用mmap函数进行IO操作,上下文进行切换
  2. DMA控制器把数据从磁盘拷贝到内核缓冲区
  3. mmap函数返回,上下文切换
  4. 应用程序调用write函数进行IO操作,上下文切换
  5. CPU从内存缓冲区把数据拷贝到socket缓冲区
  6. DMA从socket缓冲区把数据拷贝到网卡
  7. write函数返回,上下文切换

上面一共进行3次拷贝,4次上下文切换

sendfile,他是在两个个文件描述符之间传输数据,是在内存空间操作,避免了用户缓冲区和内核缓冲区的数据拷贝,

  1. 应用程序发起sendfiles函数,进行上下文切换
  2. DMA从磁盘把数据拷贝到内存缓冲区
  3. cpu从内核缓冲区拷贝到socket缓冲区
  4. DMA在异步从socket缓冲区拷贝到网卡
  5. sendfile函数返回,上下文切换

上面进行了3次数据拷贝,2次上下文切换

带有DMA拷贝功能的sendfile,linux2.4版本对sendfile进行优化,引入了SG-DMA技术,即直接可以从内存缓冲区拷贝数据到网卡,减少了数据的拷贝

  1. 应用调用sendfile函数进行上下文切换
  2. DMA从磁盘把数据拷贝到内存缓冲区
  3. CPU直接把文件描述符(包括内核缓冲区的内存地址和偏移量)发送到socket缓冲区中
  4. DMA根据文件描述符直接把数据从内核缓冲区拷贝数据到网卡中
  5. sendfile返回,上下文切换

上面一共发送了2次拷贝和2次上下文切换,全程没有cpu拷贝数据,真正实现了零拷贝,都是由DMA进行数据拷贝

0 人点赞